mirror of
https://github.com/sst/opencode.git
synced 2025-12-23 10:11:41 +00:00
sync
This commit is contained in:
parent
85a7cf8e98
commit
961de10110
3 changed files with 117 additions and 52 deletions
20
bun.lock
20
bun.lock
|
|
@ -143,8 +143,8 @@
|
|||
"@openauthjs/openauth": "catalog:",
|
||||
"@opencode-ai/plugin": "workspace:*",
|
||||
"@opencode-ai/sdk": "workspace:*",
|
||||
"@opentui/core": "0.1.24",
|
||||
"@opentui/solid": "0.1.24",
|
||||
"@opentui/core": "0.0.0-20250922-8d3a9252",
|
||||
"@opentui/solid": "0.0.0-20250922-8d3a9252",
|
||||
"@standard-schema/spec": "1.0.0",
|
||||
"@zip.js/zip.js": "2.7.62",
|
||||
"ai": "catalog:",
|
||||
|
|
@ -751,21 +751,21 @@
|
|||
|
||||
"@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.28.0", "", {}, "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA=="],
|
||||
|
||||
"@opentui/core": ["@opentui/core@0.1.24", "", { "optionalDependencies": { "@dimforge/rapier2d-simd-compat": "^0.17.3", "@opentui/core-darwin-arm64": "0.1.24", "@opentui/core-darwin-x64": "0.1.24", "@opentui/core-linux-arm64": "0.1.24", "@opentui/core-linux-x64": "0.1.24", "@opentui/core-win32-arm64": "0.1.24", "@opentui/core-win32-x64": "0.1.24", "bun-webgpu": "0.1.3", "planck": "^1.4.2", "three": "0.177.0" } }, "sha512-AbbrIvXxei9j9xfwQfe4TU+2P+x3MPIuaR1H0n01/jXmuCZGzl6Nl3xsSXC5iGTwaC6+RLsDnDBjNTENeIvetw=="],
|
||||
"@opentui/core": ["@opentui/core@0.0.0-20250922-8d3a9252", "", { "optionalDependencies": { "@dimforge/rapier2d-simd-compat": "^0.17.3", "@opentui/core-darwin-arm64": "0.0.0-20250922-8d3a9252", "@opentui/core-darwin-x64": "0.0.0-20250922-8d3a9252", "@opentui/core-linux-arm64": "0.0.0-20250922-8d3a9252", "@opentui/core-linux-x64": "0.0.0-20250922-8d3a9252", "@opentui/core-win32-arm64": "0.0.0-20250922-8d3a9252", "@opentui/core-win32-x64": "0.0.0-20250922-8d3a9252", "bun-webgpu": "0.1.3", "planck": "^1.4.2", "three": "0.177.0" } }, "sha512-fHj/CSQZgwclIwGTqDdiw01N6iTWndQvUj+IuMxSXoiJQptwo/qGQjrnvM/QzC893KxEM8kEXIpk2Xgs0vcgRQ=="],
|
||||
|
||||
"@opentui/core-darwin-arm64": ["@opentui/core-darwin-arm64@0.1.24", "", { "os": "darwin", "cpu": "arm64" }, "sha512-TLJkdIBeDdLA6SMAFGD9mi9ItcHa2eGqePt8B0DQr04M7kbe+Okqj7Y4Kuz2PJc5VJ+7MNowFwSjN1DPPjbpXw=="],
|
||||
"@opentui/core-darwin-arm64": ["@opentui/core-darwin-arm64@0.0.0-20250922-8d3a9252", "", { "os": "darwin", "cpu": "arm64" }, "sha512-D2Y42PGNCbzWHgTw3m++hYg5iPea76jpPjdSLuawZcvcIeygm2OYa20MbuYAsirKjEhKUacGsl4KFpVNmXnbmw=="],
|
||||
|
||||
"@opentui/core-darwin-x64": ["@opentui/core-darwin-x64@0.1.24", "", { "os": "darwin", "cpu": "x64" }, "sha512-qRW0HGdCbETIw/CHQ6jevcP/+ovi+kttOOgJW0Cr2OOAltOLfi/4jkPNAwQL3H2UslGEOGGYgbbpYFKCupUZ5Q=="],
|
||||
"@opentui/core-darwin-x64": ["@opentui/core-darwin-x64@0.0.0-20250922-8d3a9252", "", { "os": "darwin", "cpu": "x64" }, "sha512-2QhY7psDp4HfkEGYyRfRfRfswLy2GTNraLtujlV88P1276OWmWtb46DgB9LOuuAUnh+OVpdTZmhAXdJFfwQpbQ=="],
|
||||
|
||||
"@opentui/core-linux-arm64": ["@opentui/core-linux-arm64@0.1.24", "", { "os": "linux", "cpu": "arm64" }, "sha512-u2fvRvOyJfkMYLPNM8X+MxPqEqHCj2VyzhaIdtON72uIfXx0MvIi/srBuFe/lxnIw6ynnZUKs0A+UR71fhQqZA=="],
|
||||
"@opentui/core-linux-arm64": ["@opentui/core-linux-arm64@0.0.0-20250922-8d3a9252", "", { "os": "linux", "cpu": "arm64" }, "sha512-hy2VxwYqWrTuzvVhcelbVFD+lrzd6PkNN34jPpjKcjcMI7sUaQNQGxxFLQqhF4o4kv3lyjQF+foWvSB2Iz+UzQ=="],
|
||||
|
||||
"@opentui/core-linux-x64": ["@opentui/core-linux-x64@0.1.24", "", { "os": "linux", "cpu": "x64" }, "sha512-hzJDdAtPgLS716qXAVT7Rlci+VegPhoy4fhBcoZQ0HH1eV/olRWuLn8uonARnS59uq4cSxamhMOYm98tFCL7zg=="],
|
||||
"@opentui/core-linux-x64": ["@opentui/core-linux-x64@0.0.0-20250922-8d3a9252", "", { "os": "linux", "cpu": "x64" }, "sha512-E1xpZLwx7Gm2LvzmyZyeBoUtMXiYonzliDBl7V/awIRZ3wBSXAae1XrvAu0vOcjPVe0VsREbwA55SSETEb4gWQ=="],
|
||||
|
||||
"@opentui/core-win32-arm64": ["@opentui/core-win32-arm64@0.1.24", "", { "os": "win32", "cpu": "arm64" }, "sha512-YzraZ5kEp+DhXuUYP//KGCbWxlEc3Zcar2swggE0ktvqIgtfo0mOQddSkbWFEoQVcVeitCTbNY023G72fe+LUg=="],
|
||||
"@opentui/core-win32-arm64": ["@opentui/core-win32-arm64@0.0.0-20250922-8d3a9252", "", { "os": "win32", "cpu": "arm64" }, "sha512-nH+UGk01zu2djfDMzOnEnlcobhv1XJD9I2MiAvZcADwzjKQvytLm9fAwyw4d9xvkuH7gDkdYk1pkWnD+eogHcw=="],
|
||||
|
||||
"@opentui/core-win32-x64": ["@opentui/core-win32-x64@0.1.24", "", { "os": "win32", "cpu": "x64" }, "sha512-IuP5j6qYiSmdCc4mDxOZqDCRjXMGwM1dYk2PVn8AlIuwsG4qZLwEi7OrupBVre3HB2/ppyodeyw33l22nImMzA=="],
|
||||
|
||||
"@opentui/solid": ["@opentui/solid@0.1.24", "", { "dependencies": { "@babel/core": "7.28.0", "@babel/preset-typescript": "7.27.1", "@opentui/core": "0.1.24", "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-eBNINWYrbuM4W0W0txmVuJuoMmVw4mySDvN1MmAB068BXllrSY1e3U7NI33+xIPGTE/ShasqCPeCVyzQWF73yw=="],
|
||||
"@opentui/solid": ["@opentui/solid@0.0.0-20250922-8d3a9252", "", { "dependencies": { "@babel/core": "7.28.0", "@babel/preset-typescript": "7.27.1", "@opentui/core": "0.0.0-20250922-8d3a9252", "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-SBQuViEyiDKrXC2mfhqQwAq+ZX1fo9YWAWId6K1e0ueTTv3HO3dsTg2vhKmlo66zAojd4mVJmWBSg1+6B3ei4A=="],
|
||||
|
||||
"@oslojs/asn1": ["@oslojs/asn1@1.0.0", "", { "dependencies": { "@oslojs/binary": "1.0.0" } }, "sha512-zw/wn0sj0j0QKbIXfIlnEcTviaCzYOY3V5rAyjR6YtOByFtJiT574+8p9Wlach0lZH9fddD4yb9laEAIl4vXQA=="],
|
||||
|
||||
|
|
@ -3117,6 +3117,8 @@
|
|||
|
||||
"@opentelemetry/instrumentation-grpc/@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.27.0", "", {}, "sha512-sAay1RrB+ONOem0OZanAR1ZI/k7yDpnOQSQmTMuGImUQb2y8EbSaCJ94FQluM74xoU03vlb2d2U90hZluL6nQg=="],
|
||||
|
||||
"@opentui/core/@opentui/core-win32-x64": ["@opentui/core-win32-x64@0.0.0-20250922-8d3a9252", "", { "os": "win32", "cpu": "x64" }, "sha512-MDdcP3xuOomY2CEdzJIrV6HYreq6bYAxPWyOdUX48NjMIRKhdg/Y4W2xLGyMHr8j0MczLt/qg96dIJ9y+N4YBA=="],
|
||||
|
||||
"@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=="],
|
||||
|
||||
"@oslojs/jwt/@oslojs/encoding": ["@oslojs/encoding@0.4.1", "", {}, "sha512-hkjo6MuIK/kQR5CrGNdAPZhS01ZCXuWDRJ187zh6qqF2+yMHZpD9fAYpX8q2bOO6Ryhl3XpCT6kUX76N8hhm4Q=="],
|
||||
|
|
|
|||
|
|
@ -36,8 +36,8 @@
|
|||
"@openauthjs/openauth": "catalog:",
|
||||
"@opencode-ai/plugin": "workspace:*",
|
||||
"@opencode-ai/sdk": "workspace:*",
|
||||
"@opentui/core": "0.1.24",
|
||||
"@opentui/solid": "0.1.24",
|
||||
"@opentui/core": "0.0.0-20250922-8d3a9252",
|
||||
"@opentui/solid": "0.0.0-20250922-8d3a9252",
|
||||
"@standard-schema/spec": "1.0.0",
|
||||
"@zip.js/zip.js": "2.7.62",
|
||||
"ai": "catalog:",
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { InputRenderable, TextAttributes, BoxRenderable, type ParsedKey } from "@opentui/core"
|
||||
import { createEffect, createMemo, createResource, For, Match, onMount, Switch } from "solid-js"
|
||||
import { createEffect, createMemo, createResource, For, Match, onMount, Show, Switch } from "solid-js"
|
||||
|
||||
import { useLocal } from "../context/local"
|
||||
import { Theme } from "../context/theme"
|
||||
|
|
@ -75,6 +75,9 @@ export function Prompt(props: PromptProps) {
|
|||
</box>
|
||||
<box paddingTop={1} paddingBottom={2} backgroundColor={Theme.backgroundElement} flexGrow={1}>
|
||||
<input
|
||||
onPaste={function (text) {
|
||||
this.insertText(text)
|
||||
}}
|
||||
onInput={(value) => {
|
||||
let diff = value.length - store.input.length
|
||||
setStore(
|
||||
|
|
@ -198,7 +201,14 @@ export function Prompt(props: PromptProps) {
|
|||
type AutocompleteRef = {
|
||||
onInput: (value: string) => void
|
||||
onKeyDown: (e: ParsedKey) => void
|
||||
visible: boolean
|
||||
visible: false | "@" | "/"
|
||||
}
|
||||
|
||||
type AutocompleteOption = {
|
||||
type: string
|
||||
value: string
|
||||
display: string
|
||||
description?: string
|
||||
}
|
||||
|
||||
function Autocomplete(props: {
|
||||
|
|
@ -212,7 +222,7 @@ function Autocomplete(props: {
|
|||
const [store, setStore] = createStore({
|
||||
index: 0,
|
||||
selected: 0,
|
||||
visible: false,
|
||||
visible: false as AutocompleteRef["visible"],
|
||||
position: { x: 0, y: 0, width: 0 },
|
||||
})
|
||||
const filter = createMemo(() => {
|
||||
|
|
@ -237,10 +247,32 @@ function Autocomplete(props: {
|
|||
},
|
||||
)
|
||||
|
||||
const commands: {
|
||||
name: string
|
||||
description: string
|
||||
}[] = {}
|
||||
|
||||
const options = createMemo(() => {
|
||||
const mixed = [...files().map((x) => ({ type: "file", value: x }))]
|
||||
const mixed: AutocompleteOption[] =
|
||||
store.visible === "@"
|
||||
? [...files().map((x) => ({ type: "file", value: x, display: x }))]
|
||||
: [
|
||||
{
|
||||
type: "command",
|
||||
value: "foo",
|
||||
description: "This is a foo command",
|
||||
display: "/foo".padEnd(10),
|
||||
},
|
||||
{
|
||||
type: "command",
|
||||
value: "model",
|
||||
description: "This is a bar command",
|
||||
display: "/model".padEnd(10),
|
||||
},
|
||||
]
|
||||
if (!filter()) return mixed
|
||||
const result = fuzzysort.go(filter(), mixed, {
|
||||
keys: ["value"],
|
||||
keys: ["display"],
|
||||
})
|
||||
return result.map((arr) => arr.obj)
|
||||
})
|
||||
|
|
@ -253,14 +285,48 @@ function Autocomplete(props: {
|
|||
function move(direction: -1 | 1) {
|
||||
if (!store.visible) return
|
||||
let next = store.selected + direction
|
||||
if (next < 0) next = files().length - 1
|
||||
if (next >= files().length) next = 0
|
||||
if (next < 0) next = options().length - 1
|
||||
if (next >= options().length) next = 0
|
||||
setStore("selected", next)
|
||||
}
|
||||
|
||||
function show() {
|
||||
function select() {
|
||||
const selected = options()[store.selected]
|
||||
if (!selected) return
|
||||
|
||||
if (selected.type === "file") {
|
||||
const part: Prompt["parts"][number] = {
|
||||
type: "file",
|
||||
mime: "text/plain",
|
||||
filename: selected.value,
|
||||
url: `file://${process.cwd()}/${selected.value}`,
|
||||
source: {
|
||||
type: "file",
|
||||
text: {
|
||||
start: store.index,
|
||||
end: store.index + selected.value.length + 1,
|
||||
value: "@" + selected,
|
||||
},
|
||||
path: selected.value,
|
||||
},
|
||||
}
|
||||
props.setPrompt((draft) => {
|
||||
const append = "@" + selected.value + " "
|
||||
if (store.index === 0) draft.input = append
|
||||
if (store.index > 0) draft.input = draft.input.slice(0, store.index) + append
|
||||
draft.parts.push(part)
|
||||
})
|
||||
}
|
||||
|
||||
if (selected.type === "command") {
|
||||
}
|
||||
|
||||
setTimeout(() => hide(), 0)
|
||||
}
|
||||
|
||||
function show(mode: "@" | "/") {
|
||||
setStore({
|
||||
visible: true,
|
||||
visible: mode,
|
||||
index: props.input().cursorPosition,
|
||||
position: {
|
||||
x: props.anchor().x,
|
||||
|
|
@ -271,6 +337,7 @@ function Autocomplete(props: {
|
|||
}
|
||||
|
||||
function hide() {
|
||||
if (store.visible === "/") props.input().value = ""
|
||||
setStore("visible", false)
|
||||
}
|
||||
|
||||
|
|
@ -287,61 +354,57 @@ function Autocomplete(props: {
|
|||
if (e.name === "up") move(-1)
|
||||
if (e.name === "down") move(1)
|
||||
if (e.name === "escape") hide()
|
||||
if (e.name === "return") {
|
||||
const file = files()[store.selected]
|
||||
if (!file) return
|
||||
const part: Prompt["parts"][number] = {
|
||||
type: "file",
|
||||
mime: "text/plain",
|
||||
filename: file,
|
||||
url: `file://${process.cwd()}/${file}`,
|
||||
source: {
|
||||
type: "file",
|
||||
text: {
|
||||
start: store.index,
|
||||
end: store.index + file.length + 1,
|
||||
value: "@" + file,
|
||||
},
|
||||
path: file,
|
||||
},
|
||||
}
|
||||
props.setPrompt((draft) => {
|
||||
const append = "@" + file + " "
|
||||
if (store.index === 0) draft.input = append
|
||||
if (store.index > 0) draft.input = draft.input.slice(0, store.index) + append
|
||||
draft.parts.push(part)
|
||||
})
|
||||
setTimeout(() => hide(), 0)
|
||||
}
|
||||
if (e.name === "return") select()
|
||||
}
|
||||
if (!store.visible && e.name === "@") {
|
||||
const last = props.value.at(-1)
|
||||
if (last === " " || last === undefined) {
|
||||
show()
|
||||
show("@")
|
||||
}
|
||||
}
|
||||
|
||||
if (!store.visible && e.name === "/") {
|
||||
if (props.input().cursorPosition === 0) show("/")
|
||||
}
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
const height = createMemo(() => {
|
||||
if (options().length) return Math.min(10, options().length)
|
||||
return 1
|
||||
})
|
||||
|
||||
return (
|
||||
<box
|
||||
visible={store.visible}
|
||||
visible={store.visible !== false}
|
||||
position="absolute"
|
||||
top={store.position.y - 10}
|
||||
top={store.position.y - height()}
|
||||
left={store.position.x}
|
||||
width={store.position.width}
|
||||
zIndex={100}
|
||||
{...SplitBorder}
|
||||
>
|
||||
<box backgroundColor={Theme.backgroundElement} height={10}>
|
||||
<For each={options()}>
|
||||
<box backgroundColor={Theme.backgroundElement} height={height()}>
|
||||
<For
|
||||
each={options()}
|
||||
fallback={
|
||||
<box paddingLeft={1} paddingRight={1}>
|
||||
<text>No matching items</text>
|
||||
</box>
|
||||
}
|
||||
>
|
||||
{(option, index) => (
|
||||
<box
|
||||
paddingLeft={1}
|
||||
paddingRight={1}
|
||||
backgroundColor={index() === store.selected ? Theme.primary : undefined}
|
||||
flexDirection="row"
|
||||
>
|
||||
<text fg={index() === store.selected ? Theme.background : Theme.text}>{option.value}</text>
|
||||
<text fg={index() === store.selected ? Theme.background : Theme.text}>{option.display}</text>
|
||||
<Show when={option.description}>
|
||||
<text fg={index() === store.selected ? Theme.background : Theme.textMuted}> {option.description}</text>
|
||||
</Show>
|
||||
</box>
|
||||
)}
|
||||
</For>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue