summaryrefslogtreecommitdiffhomepage
path: root/packages/app
diff options
context:
space:
mode:
authorAdam <[email protected]>2026-02-02 11:46:25 -0600
committerAdam <[email protected]>2026-02-02 14:24:24 -0600
commit70cf609ce90a7534349c8dd5ed8441cbd32ebba7 (patch)
tree4dbf2be3e1928c3c4414fe3397eaf90d076d0c63 /packages/app
parent2f76b49df3cfd316069a2b5c292fed369acadbde (diff)
downloadopencode-70cf609ce90a7534349c8dd5ed8441cbd32ebba7.tar.gz
opencode-70cf609ce90a7534349c8dd5ed8441cbd32ebba7.zip
Revert "feat(ui): Select, dropdown, popover styles & transitions (#11675)"
This reverts commit 377bf7ff21a4f05807c38675ac70cd08fe67b516.
Diffstat (limited to 'packages/app')
-rw-r--r--packages/app/src/components/dialog-select-model.tsx7
-rw-r--r--packages/app/src/components/prompt-input.tsx125
-rw-r--r--packages/app/src/components/settings-general.tsx2
3 files changed, 46 insertions, 88 deletions
diff --git a/packages/app/src/components/dialog-select-model.tsx b/packages/app/src/components/dialog-select-model.tsx
index 2135b1edf..4f0dcc3ee 100644
--- a/packages/app/src/components/dialog-select-model.tsx
+++ b/packages/app/src/components/dialog-select-model.tsx
@@ -90,10 +90,9 @@ const ModelList: Component<{
export function ModelSelectorPopover<T extends ValidComponent = "div">(props: {
provider?: string
- children?: JSX.Element | ((open: boolean) => JSX.Element)
+ children?: JSX.Element
triggerAs?: T
triggerProps?: ComponentProps<T>
- gutter?: number
}) {
const [store, setStore] = createStore<{
open: boolean
@@ -176,14 +175,14 @@ export function ModelSelectorPopover<T extends ValidComponent = "div">(props: {
}}
modal={false}
placement="top-start"
- gutter={props.gutter ?? 8}
+ gutter={8}
>
<Kobalte.Trigger
ref={(el) => setStore("trigger", el)}
as={props.triggerAs ?? "div"}
{...(props.triggerProps as any)}
>
- {typeof props.children === "function" ? props.children(store.open) : props.children}
+ {props.children}
</Kobalte.Trigger>
<Kobalte.Portal>
<Kobalte.Content
diff --git a/packages/app/src/components/prompt-input.tsx b/packages/app/src/components/prompt-input.tsx
index d31d0b2a3..5162c0b08 100644
--- a/packages/app/src/components/prompt-input.tsx
+++ b/packages/app/src/components/prompt-input.tsx
@@ -32,9 +32,7 @@ import { useNavigate, useParams } from "@solidjs/router"
import { useSync } from "@/context/sync"
import { useComments } from "@/context/comments"
import { FileIcon } from "@opencode-ai/ui/file-icon"
-import { MorphChevron } from "@opencode-ai/ui/morph-chevron"
import { Button } from "@opencode-ai/ui/button"
-import { CycleLabel } from "@opencode-ai/ui/cycle-label"
import { Icon } from "@opencode-ai/ui/icon"
import { ProviderIcon } from "@opencode-ai/ui/provider-icon"
import type { IconName } from "@opencode-ai/ui/icons/provider"
@@ -44,7 +42,6 @@ import { Select } from "@opencode-ai/ui/select"
import { getDirectory, getFilename, getFilenameTruncated } from "@opencode-ai/util/path"
import { useDialog } from "@opencode-ai/ui/context/dialog"
import { ImagePreview } from "@opencode-ai/ui/image-preview"
-import { ReasoningIcon } from "@opencode-ai/ui/reasoning-icon"
import { ModelSelectorPopover } from "@/components/dialog-select-model"
import { DialogSelectModelUnpaid } from "@/components/dialog-select-model-unpaid"
import { useProviders } from "@/hooks/use-providers"
@@ -1257,7 +1254,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
clearInput()
client.session
.shell({
- sessionID: session?.id || "",
+ sessionID: session.id,
agent,
model,
command: text,
@@ -1280,7 +1277,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
clearInput()
client.session
.command({
- sessionID: session?.id || "",
+ sessionID: session.id,
command: commandName,
arguments: args.join(" "),
agent,
@@ -1436,13 +1433,13 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
const optimisticParts = requestParts.map((part) => ({
...part,
- sessionID: session?.id || "",
+ sessionID: session.id,
messageID,
})) as unknown as Part[]
const optimisticMessage: Message = {
id: messageID,
- sessionID: session?.id || "",
+ sessionID: session.id,
role: "user",
time: { created: Date.now() },
agent,
@@ -1453,9 +1450,9 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
if (sessionDirectory === projectDirectory) {
sync.set(
produce((draft) => {
- const messages = draft.message[session?.id || ""]
+ const messages = draft.message[session.id]
if (!messages) {
- draft.message[session?.id || ""] = [optimisticMessage]
+ draft.message[session.id] = [optimisticMessage]
} else {
const result = Binary.search(messages, messageID, (m) => m.id)
messages.splice(result.index, 0, optimisticMessage)
@@ -1471,9 +1468,9 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
globalSync.child(sessionDirectory)[1](
produce((draft) => {
- const messages = draft.message[session?.id || ""]
+ const messages = draft.message[session.id]
if (!messages) {
- draft.message[session?.id || ""] = [optimisticMessage]
+ draft.message[session.id] = [optimisticMessage]
} else {
const result = Binary.search(messages, messageID, (m) => m.id)
messages.splice(result.index, 0, optimisticMessage)
@@ -1490,7 +1487,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
if (sessionDirectory === projectDirectory) {
sync.set(
produce((draft) => {
- const messages = draft.message[session?.id || ""]
+ const messages = draft.message[session.id]
if (messages) {
const result = Binary.search(messages, messageID, (m) => m.id)
if (result.found) messages.splice(result.index, 1)
@@ -1503,7 +1500,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
globalSync.child(sessionDirectory)[1](
produce((draft) => {
- const messages = draft.message[session?.id || ""]
+ const messages = draft.message[session.id]
if (messages) {
const result = Binary.search(messages, messageID, (m) => m.id)
if (result.found) messages.splice(result.index, 1)
@@ -1524,15 +1521,15 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
const worktree = WorktreeState.get(sessionDirectory)
if (!worktree || worktree.status !== "pending") return true
- if (sessionDirectory === projectDirectory && session?.id) {
- sync.set("session_status", session?.id, { type: "busy" })
+ if (sessionDirectory === projectDirectory) {
+ sync.set("session_status", session.id, { type: "busy" })
}
const controller = new AbortController()
const cleanup = () => {
- if (sessionDirectory === projectDirectory && session?.id) {
- sync.set("session_status", session?.id, { type: "idle" })
+ if (sessionDirectory === projectDirectory) {
+ sync.set("session_status", session.id, { type: "idle" })
}
removeOptimisticMessage()
for (const item of commentItems) {
@@ -1549,7 +1546,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
restoreInput()
}
- pending.set(session?.id || "", { abort: controller, cleanup })
+ pending.set(session.id, { abort: controller, cleanup })
const abort = new Promise<Awaited<ReturnType<typeof WorktreeState.wait>>>((resolve) => {
if (controller.signal.aborted) {
@@ -1577,7 +1574,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
if (timer.id === undefined) return
clearTimeout(timer.id)
})
- pending.delete(session?.id || "")
+ pending.delete(session.id)
if (controller.signal.aborted) return false
if (result.status === "failed") throw new Error(result.message)
return true
@@ -1587,7 +1584,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
const ok = await waitForWorktree()
if (!ok) return
await client.session.prompt({
- sessionID: session?.id || "",
+ sessionID: session.id,
agent,
model,
messageID,
@@ -1597,9 +1594,9 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
}
void send().catch((err) => {
- pending.delete(session?.id || "")
- if (sessionDirectory === projectDirectory && session?.id) {
- sync.set("session_status", session?.id, { type: "idle" })
+ pending.delete(session.id)
+ if (sessionDirectory === projectDirectory) {
+ sync.set("session_status", session.id, { type: "idle" })
}
showToast({
title: language.t("prompt.toast.promptSendFailed.title"),
@@ -1621,28 +1618,6 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
})
}
- const currrentModelVariant = createMemo(() => {
- const modelVariant = local.model.variant.current() ?? ""
- return modelVariant === "xhigh"
- ? "xHigh"
- : modelVariant.length > 0
- ? modelVariant[0].toUpperCase() + modelVariant.slice(1)
- : "Default"
- })
-
- const reasoningPercentage = createMemo(() => {
- const variants = local.model.variant.list()
- const current = local.model.variant.current()
- const totalEntries = variants.length + 1
-
- if (totalEntries <= 2 || current === "Default") {
- return 0
- }
-
- const currentIndex = current ? variants.indexOf(current) + 1 : 0
- return ((currentIndex + 1) / totalEntries) * 100
- }, [local.model.variant])
-
return (
<div class="relative size-full _max-h-[320px] flex flex-col gap-3">
<Show when={store.popover}>
@@ -1695,7 +1670,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
</>
}
>
- <Icon name="brain" size="normal" class="text-icon-info-active shrink-0" />
+ <Icon name="brain" size="small" class="text-icon-info-active shrink-0" />
<span class="text-14-regular text-text-strong whitespace-nowrap">
@{(item as { type: "agent"; name: string }).name}
</span>
@@ -1760,9 +1735,9 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
}}
>
<Show when={store.dragging}>
- <div class="absolute inset-0 z-10 flex items-center justify-center bg-surface-raised-stronger-non-alpha/90 mr-1 pointer-events-none">
+ <div class="absolute inset-0 z-10 flex items-center justify-center bg-surface-raised-stronger-non-alpha/90 pointer-events-none">
<div class="flex flex-col items-center gap-2 text-text-weak">
- <Icon name="photo" size={18} class="text-icon-base stroke-1.5" />
+ <Icon name="photo" class="size-8" />
<span class="text-14-regular">{language.t("prompt.dropzone.label")}</span>
</div>
</div>
@@ -1848,7 +1823,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
when={attachment.mime.startsWith("image/")}
fallback={
<div class="size-16 rounded-md bg-surface-base flex items-center justify-center border border-border-base">
- <Icon name="folder" size="normal" class="size-6 text-text-base" />
+ <Icon name="folder" class="size-6 text-text-weak" />
</div>
}
>
@@ -1922,7 +1897,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
</Show>
</div>
<div class="relative p-3 flex items-center justify-between">
- <div class="flex items-center justify-start gap-2">
+ <div class="flex items-center justify-start gap-0.5">
<Switch>
<Match when={store.mode === "shell"}>
<div class="flex items-center gap-2 px-2 h-6">
@@ -1943,7 +1918,6 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
onSelect={local.agent.set}
class="capitalize"
variant="ghost"
- gutter={12}
/>
</TooltipKeybind>
<Show
@@ -1954,19 +1928,12 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
title={language.t("command.model.choose")}
keybind={command.keybind("model.choose")}
>
- <Button
- as="div"
- variant="ghost"
- class="px-2"
- onClick={() => dialog.show(() => <DialogSelectModelUnpaid />)}
- >
+ <Button as="div" variant="ghost" onClick={() => dialog.show(() => <DialogSelectModelUnpaid />)}>
<Show when={local.model.current()?.provider?.id}>
<ProviderIcon id={local.model.current()!.provider.id as IconName} class="size-4 shrink-0" />
</Show>
{local.model.current()?.name ?? language.t("dialog.model.select.title")}
- <MorphChevron
- expanded={!!dialog.active?.id && dialog.active.id.startsWith("select-model-unpaid")}
- />
+ <Icon name="chevron-down" size="small" />
</Button>
</TooltipKeybind>
}
@@ -1976,16 +1943,12 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
title={language.t("command.model.choose")}
keybind={command.keybind("model.choose")}
>
- <ModelSelectorPopover triggerAs={Button} triggerProps={{ variant: "ghost" }} gutter={12}>
- {(open) => (
- <>
- <Show when={local.model.current()?.provider?.id}>
- <ProviderIcon id={local.model.current()!.provider.id as IconName} class="size-4 shrink-0" />
- </Show>
- {local.model.current()?.name ?? language.t("dialog.model.select.title")}
- <MorphChevron expanded={open} class="text-text-weak" />
- </>
- )}
+ <ModelSelectorPopover triggerAs={Button} triggerProps={{ variant: "ghost" }}>
+ <Show when={local.model.current()?.provider?.id}>
+ <ProviderIcon id={local.model.current()!.provider.id as IconName} class="size-4 shrink-0" />
+ </Show>
+ {local.model.current()?.name ?? language.t("dialog.model.select.title")}
+ <Icon name="chevron-down" size="small" />
</ModelSelectorPopover>
</TooltipKeybind>
</Show>
@@ -1998,13 +1961,10 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
<Button
data-action="model-variant-cycle"
variant="ghost"
- class="text-text-strong text-12-regular"
+ class="text-text-base _hidden group-hover/prompt-input:inline-block capitalize text-12-regular"
onClick={() => local.model.variant.cycle()}
>
- <Show when={local.model.variant.list().length > 1}>
- <ReasoningIcon percentage={reasoningPercentage()} size={16} strokeWidth={1.25} />
- </Show>
- <CycleLabel value={currrentModelVariant()} />
+ {local.model.variant.current() ?? language.t("common.default")}
</Button>
</TooltipKeybind>
</Show>
@@ -2018,7 +1978,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
variant="ghost"
onClick={() => permission.toggleAutoAccept(params.id!, sdk.directory)}
classList={{
- "_hidden group-hover/prompt-input:flex items-center justify-center": true,
+ "_hidden group-hover/prompt-input:flex size-6 items-center justify-center": true,
"text-text-base": !permission.isAutoAccepting(params.id!, sdk.directory),
"hover:bg-surface-success-base": permission.isAutoAccepting(params.id!, sdk.directory),
}}
@@ -2040,7 +2000,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
</Match>
</Switch>
</div>
- <div class="flex items-center gap-1 absolute right-3 bottom-3">
+ <div class="flex items-center gap-3 absolute right-3 bottom-3">
<input
ref={fileInputRef}
type="file"
@@ -2052,19 +2012,18 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
e.currentTarget.value = ""
}}
/>
- <div class="flex items-center gap-1.5 mr-1.5">
+ <div class="flex items-center gap-2">
<SessionContextUsage />
<Show when={store.mode === "normal"}>
<Tooltip placement="top" value={language.t("prompt.action.attachFile")}>
<Button
type="button"
variant="ghost"
- size="small"
- class="px-1"
+ class="size-6"
onClick={() => fileInputRef.click()}
aria-label={language.t("prompt.action.attachFile")}
>
- <Icon name="photo" class="size-6 text-icon-base" />
+ <Icon name="photo" class="size-4.5" />
</Button>
</Tooltip>
</Show>
@@ -2083,7 +2042,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
<Match when={true}>
<div class="flex items-center gap-2">
<span>{language.t("prompt.action.send")}</span>
- <Icon name="enter" size="normal" class="text-icon-base" />
+ <Icon name="enter" size="small" class="text-icon-base" />
</div>
</Match>
</Switch>
@@ -2094,7 +2053,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
disabled={!prompt.dirty() && !working()}
icon={working() ? "stop" : "arrow-up"}
variant="primary"
- class="h-6 w-5.5"
+ class="h-6 w-4.5"
aria-label={working() ? language.t("prompt.action.stop") : language.t("prompt.action.send")}
/>
</Tooltip>
diff --git a/packages/app/src/components/settings-general.tsx b/packages/app/src/components/settings-general.tsx
index 94813871e..b31cfb6cc 100644
--- a/packages/app/src/components/settings-general.tsx
+++ b/packages/app/src/components/settings-general.tsx
@@ -226,7 +226,7 @@ export const SettingsGeneral: Component = () => {
variant="secondary"
size="small"
triggerVariant="settings"
- triggerStyle={{ "font-family": monoFontFamily(settings.appearance.font()), "field-sizing": "content" }}
+ triggerStyle={{ "font-family": monoFontFamily(settings.appearance.font()), "min-width": "180px" }}
>
{(option) => (
<span style={{ "font-family": monoFontFamily(option?.value) }}>