mirror of
https://github.com/anomalyco/opencode.git
synced 2026-04-30 09:46:35 +00:00
feat: prompt styles
This commit is contained in:
@@ -90,9 +90,10 @@ const ModelList: Component<{
|
|||||||
|
|
||||||
export function ModelSelectorPopover<T extends ValidComponent = "div">(props: {
|
export function ModelSelectorPopover<T extends ValidComponent = "div">(props: {
|
||||||
provider?: string
|
provider?: string
|
||||||
children?: JSX.Element
|
children?: JSX.Element | ((open: boolean) => JSX.Element)
|
||||||
triggerAs?: T
|
triggerAs?: T
|
||||||
triggerProps?: ComponentProps<T>
|
triggerProps?: ComponentProps<T>,
|
||||||
|
gutter?: number
|
||||||
}) {
|
}) {
|
||||||
const [store, setStore] = createStore<{
|
const [store, setStore] = createStore<{
|
||||||
open: boolean
|
open: boolean
|
||||||
@@ -175,14 +176,14 @@ export function ModelSelectorPopover<T extends ValidComponent = "div">(props: {
|
|||||||
}}
|
}}
|
||||||
modal={false}
|
modal={false}
|
||||||
placement="top-start"
|
placement="top-start"
|
||||||
gutter={8}
|
gutter={props.gutter ?? 8}
|
||||||
>
|
>
|
||||||
<Kobalte.Trigger
|
<Kobalte.Trigger
|
||||||
ref={(el) => setStore("trigger", el)}
|
ref={(el) => setStore("trigger", el)}
|
||||||
as={props.triggerAs ?? "div"}
|
as={props.triggerAs ?? "div"}
|
||||||
{...(props.triggerProps as any)}
|
{...(props.triggerProps as any)}
|
||||||
>
|
>
|
||||||
{props.children}
|
{typeof props.children === "function" ? props.children(store.open) : props.children}
|
||||||
</Kobalte.Trigger>
|
</Kobalte.Trigger>
|
||||||
<Kobalte.Portal>
|
<Kobalte.Portal>
|
||||||
<Kobalte.Content
|
<Kobalte.Content
|
||||||
|
|||||||
@@ -32,7 +32,9 @@ import { useNavigate, useParams } from "@solidjs/router"
|
|||||||
import { useSync } from "@/context/sync"
|
import { useSync } from "@/context/sync"
|
||||||
import { useComments } from "@/context/comments"
|
import { useComments } from "@/context/comments"
|
||||||
import { FileIcon } from "@opencode-ai/ui/file-icon"
|
import { FileIcon } from "@opencode-ai/ui/file-icon"
|
||||||
|
import { MorphChevron } from "@opencode-ai/ui/morph-chevron"
|
||||||
import { Button } from "@opencode-ai/ui/button"
|
import { Button } from "@opencode-ai/ui/button"
|
||||||
|
import { CycleLabel } from "@opencode-ai/ui/cycle-label"
|
||||||
import { Icon } from "@opencode-ai/ui/icon"
|
import { Icon } from "@opencode-ai/ui/icon"
|
||||||
import { ProviderIcon } from "@opencode-ai/ui/provider-icon"
|
import { ProviderIcon } from "@opencode-ai/ui/provider-icon"
|
||||||
import type { IconName } from "@opencode-ai/ui/icons/provider"
|
import type { IconName } from "@opencode-ai/ui/icons/provider"
|
||||||
@@ -42,6 +44,7 @@ import { Select } from "@opencode-ai/ui/select"
|
|||||||
import { getDirectory, getFilename, getFilenameTruncated } from "@opencode-ai/util/path"
|
import { getDirectory, getFilename, getFilenameTruncated } from "@opencode-ai/util/path"
|
||||||
import { useDialog } from "@opencode-ai/ui/context/dialog"
|
import { useDialog } from "@opencode-ai/ui/context/dialog"
|
||||||
import { ImagePreview } from "@opencode-ai/ui/image-preview"
|
import { ImagePreview } from "@opencode-ai/ui/image-preview"
|
||||||
|
import { ReasoningIcon } from "@opencode-ai/ui/reasoning-icon"
|
||||||
import { ModelSelectorPopover } from "@/components/dialog-select-model"
|
import { ModelSelectorPopover } from "@/components/dialog-select-model"
|
||||||
import { DialogSelectModelUnpaid } from "@/components/dialog-select-model-unpaid"
|
import { DialogSelectModelUnpaid } from "@/components/dialog-select-model-unpaid"
|
||||||
import { useProviders } from "@/hooks/use-providers"
|
import { useProviders } from "@/hooks/use-providers"
|
||||||
@@ -922,7 +925,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
.abort({
|
.abort({
|
||||||
sessionID,
|
sessionID,
|
||||||
})
|
})
|
||||||
.catch(() => {})
|
.catch(() => { })
|
||||||
}
|
}
|
||||||
|
|
||||||
const addToHistory = (prompt: Prompt, mode: "normal" | "shell") => {
|
const addToHistory = (prompt: Prompt, mode: "normal" | "shell") => {
|
||||||
@@ -1252,7 +1255,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
clearInput()
|
clearInput()
|
||||||
client.session
|
client.session
|
||||||
.shell({
|
.shell({
|
||||||
sessionID: session.id,
|
sessionID: session?.id || "",
|
||||||
agent,
|
agent,
|
||||||
model,
|
model,
|
||||||
command: text,
|
command: text,
|
||||||
@@ -1275,7 +1278,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
clearInput()
|
clearInput()
|
||||||
client.session
|
client.session
|
||||||
.command({
|
.command({
|
||||||
sessionID: session.id,
|
sessionID: session?.id || "",
|
||||||
command: commandName,
|
command: commandName,
|
||||||
arguments: args.join(" "),
|
arguments: args.join(" "),
|
||||||
agent,
|
agent,
|
||||||
@@ -1431,13 +1434,13 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
|
|
||||||
const optimisticParts = requestParts.map((part) => ({
|
const optimisticParts = requestParts.map((part) => ({
|
||||||
...part,
|
...part,
|
||||||
sessionID: session.id,
|
sessionID: session?.id || "",
|
||||||
messageID,
|
messageID,
|
||||||
})) as unknown as Part[]
|
})) as unknown as Part[]
|
||||||
|
|
||||||
const optimisticMessage: Message = {
|
const optimisticMessage: Message = {
|
||||||
id: messageID,
|
id: messageID,
|
||||||
sessionID: session.id,
|
sessionID: session?.id || "",
|
||||||
role: "user",
|
role: "user",
|
||||||
time: { created: Date.now() },
|
time: { created: Date.now() },
|
||||||
agent,
|
agent,
|
||||||
@@ -1448,9 +1451,9 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
if (sessionDirectory === projectDirectory) {
|
if (sessionDirectory === projectDirectory) {
|
||||||
sync.set(
|
sync.set(
|
||||||
produce((draft) => {
|
produce((draft) => {
|
||||||
const messages = draft.message[session.id]
|
const messages = draft.message[session?.id || ""]
|
||||||
if (!messages) {
|
if (!messages) {
|
||||||
draft.message[session.id] = [optimisticMessage]
|
draft.message[session?.id || ""] = [optimisticMessage]
|
||||||
} else {
|
} else {
|
||||||
const result = Binary.search(messages, messageID, (m) => m.id)
|
const result = Binary.search(messages, messageID, (m) => m.id)
|
||||||
messages.splice(result.index, 0, optimisticMessage)
|
messages.splice(result.index, 0, optimisticMessage)
|
||||||
@@ -1466,9 +1469,9 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
|
|
||||||
globalSync.child(sessionDirectory)[1](
|
globalSync.child(sessionDirectory)[1](
|
||||||
produce((draft) => {
|
produce((draft) => {
|
||||||
const messages = draft.message[session.id]
|
const messages = draft.message[session?.id || ""]
|
||||||
if (!messages) {
|
if (!messages) {
|
||||||
draft.message[session.id] = [optimisticMessage]
|
draft.message[session?.id || ""] = [optimisticMessage]
|
||||||
} else {
|
} else {
|
||||||
const result = Binary.search(messages, messageID, (m) => m.id)
|
const result = Binary.search(messages, messageID, (m) => m.id)
|
||||||
messages.splice(result.index, 0, optimisticMessage)
|
messages.splice(result.index, 0, optimisticMessage)
|
||||||
@@ -1485,7 +1488,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
if (sessionDirectory === projectDirectory) {
|
if (sessionDirectory === projectDirectory) {
|
||||||
sync.set(
|
sync.set(
|
||||||
produce((draft) => {
|
produce((draft) => {
|
||||||
const messages = draft.message[session.id]
|
const messages = draft.message[session?.id || ""]
|
||||||
if (messages) {
|
if (messages) {
|
||||||
const result = Binary.search(messages, messageID, (m) => m.id)
|
const result = Binary.search(messages, messageID, (m) => m.id)
|
||||||
if (result.found) messages.splice(result.index, 1)
|
if (result.found) messages.splice(result.index, 1)
|
||||||
@@ -1498,7 +1501,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
|
|
||||||
globalSync.child(sessionDirectory)[1](
|
globalSync.child(sessionDirectory)[1](
|
||||||
produce((draft) => {
|
produce((draft) => {
|
||||||
const messages = draft.message[session.id]
|
const messages = draft.message[session?.id || ""]
|
||||||
if (messages) {
|
if (messages) {
|
||||||
const result = Binary.search(messages, messageID, (m) => m.id)
|
const result = Binary.search(messages, messageID, (m) => m.id)
|
||||||
if (result.found) messages.splice(result.index, 1)
|
if (result.found) messages.splice(result.index, 1)
|
||||||
@@ -1519,15 +1522,15 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
const worktree = WorktreeState.get(sessionDirectory)
|
const worktree = WorktreeState.get(sessionDirectory)
|
||||||
if (!worktree || worktree.status !== "pending") return true
|
if (!worktree || worktree.status !== "pending") return true
|
||||||
|
|
||||||
if (sessionDirectory === projectDirectory) {
|
if (sessionDirectory === projectDirectory && session?.id) {
|
||||||
sync.set("session_status", session.id, { type: "busy" })
|
sync.set("session_status", session?.id, { type: "busy" })
|
||||||
}
|
}
|
||||||
|
|
||||||
const controller = new AbortController()
|
const controller = new AbortController()
|
||||||
|
|
||||||
const cleanup = () => {
|
const cleanup = () => {
|
||||||
if (sessionDirectory === projectDirectory) {
|
if (sessionDirectory === projectDirectory && session?.id) {
|
||||||
sync.set("session_status", session.id, { type: "idle" })
|
sync.set("session_status", session?.id, { type: "idle" })
|
||||||
}
|
}
|
||||||
removeOptimisticMessage()
|
removeOptimisticMessage()
|
||||||
for (const item of commentItems) {
|
for (const item of commentItems) {
|
||||||
@@ -1544,7 +1547,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
restoreInput()
|
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) => {
|
const abort = new Promise<Awaited<ReturnType<typeof WorktreeState.wait>>>((resolve) => {
|
||||||
if (controller.signal.aborted) {
|
if (controller.signal.aborted) {
|
||||||
@@ -1572,7 +1575,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
if (timer.id === undefined) return
|
if (timer.id === undefined) return
|
||||||
clearTimeout(timer.id)
|
clearTimeout(timer.id)
|
||||||
})
|
})
|
||||||
pending.delete(session.id)
|
pending.delete(session?.id || "")
|
||||||
if (controller.signal.aborted) return false
|
if (controller.signal.aborted) return false
|
||||||
if (result.status === "failed") throw new Error(result.message)
|
if (result.status === "failed") throw new Error(result.message)
|
||||||
return true
|
return true
|
||||||
@@ -1582,7 +1585,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
const ok = await waitForWorktree()
|
const ok = await waitForWorktree()
|
||||||
if (!ok) return
|
if (!ok) return
|
||||||
await client.session.prompt({
|
await client.session.prompt({
|
||||||
sessionID: session.id,
|
sessionID: session?.id || "",
|
||||||
agent,
|
agent,
|
||||||
model,
|
model,
|
||||||
messageID,
|
messageID,
|
||||||
@@ -1592,9 +1595,9 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void send().catch((err) => {
|
void send().catch((err) => {
|
||||||
pending.delete(session.id)
|
pending.delete(session?.id || "")
|
||||||
if (sessionDirectory === projectDirectory) {
|
if (sessionDirectory === projectDirectory && session?.id) {
|
||||||
sync.set("session_status", session.id, { type: "idle" })
|
sync.set("session_status", session?.id, { type: "idle" })
|
||||||
}
|
}
|
||||||
showToast({
|
showToast({
|
||||||
title: language.t("prompt.toast.promptSendFailed.title"),
|
title: language.t("prompt.toast.promptSendFailed.title"),
|
||||||
@@ -1616,6 +1619,28 @@ 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 (
|
return (
|
||||||
<div class="relative size-full _max-h-[320px] flex flex-col gap-3">
|
<div class="relative size-full _max-h-[320px] flex flex-col gap-3">
|
||||||
<Show when={store.popover}>
|
<Show when={store.popover}>
|
||||||
@@ -1668,7 +1693,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Icon name="brain" size="small" class="text-icon-info-active shrink-0" />
|
<Icon name="brain" size="normal" class="text-icon-info-active shrink-0" />
|
||||||
<span class="text-14-regular text-text-strong whitespace-nowrap">
|
<span class="text-14-regular text-text-strong whitespace-nowrap">
|
||||||
@{(item as { type: "agent"; name: string }).name}
|
@{(item as { type: "agent"; name: string }).name}
|
||||||
</span>
|
</span>
|
||||||
@@ -1729,9 +1754,9 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Show when={store.dragging}>
|
<Show when={store.dragging}>
|
||||||
<div class="absolute inset-0 z-10 flex items-center justify-center bg-surface-raised-stronger-non-alpha/90 pointer-events-none">
|
<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="flex flex-col items-center gap-2 text-text-weak">
|
<div class="flex flex-col items-center gap-2 text-text-weak">
|
||||||
<Icon name="photo" class="size-8" />
|
<Icon name="photo" size={18} class="text-icon-base stroke-1.5" />
|
||||||
<span class="text-14-regular">{language.t("prompt.dropzone.label")}</span>
|
<span class="text-14-regular">{language.t("prompt.dropzone.label")}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -1770,7 +1795,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div class="flex items-center gap-1.5">
|
<div class="flex items-center gap-1.5">
|
||||||
<FileIcon node={{ path: item.path, type: "file" }} class="shrink-0 size-3.5" />
|
<FileIcon node={{ path: item.path, type: "file" }} class="shrink-0 size-7" />
|
||||||
<div class="flex items-center text-11-regular min-w-0 font-medium">
|
<div class="flex items-center text-11-regular min-w-0 font-medium">
|
||||||
<span class="text-text-strong whitespace-nowrap">{getFilenameTruncated(item.path, 14)}</span>
|
<span class="text-text-strong whitespace-nowrap">{getFilenameTruncated(item.path, 14)}</span>
|
||||||
<Show when={item.selection}>
|
<Show when={item.selection}>
|
||||||
@@ -1787,7 +1812,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
type="button"
|
type="button"
|
||||||
icon="close-small"
|
icon="close-small"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
class="ml-auto h-5 w-5 opacity-0 group-hover:opacity-100 transition-all"
|
class="ml-auto size-7 opacity-0 group-hover:opacity-100 transition-all"
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
if (item.commentID) comments.remove(item.path, item.commentID)
|
if (item.commentID) comments.remove(item.path, item.commentID)
|
||||||
@@ -1817,7 +1842,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
when={attachment.mime.startsWith("image/")}
|
when={attachment.mime.startsWith("image/")}
|
||||||
fallback={
|
fallback={
|
||||||
<div class="size-16 rounded-md bg-surface-base flex items-center justify-center border border-border-base">
|
<div class="size-16 rounded-md bg-surface-base flex items-center justify-center border border-border-base">
|
||||||
<Icon name="folder" class="size-6 text-text-weak" />
|
<Icon name="folder" size="normal" class="size-6 text-text-base" />
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
@@ -1891,7 +1916,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
</Show>
|
</Show>
|
||||||
</div>
|
</div>
|
||||||
<div class="relative p-3 flex items-center justify-between">
|
<div class="relative p-3 flex items-center justify-between">
|
||||||
<div class="flex items-center justify-start gap-0.5">
|
<div class="flex items-center justify-start gap-2">
|
||||||
<Switch>
|
<Switch>
|
||||||
<Match when={store.mode === "shell"}>
|
<Match when={store.mode === "shell"}>
|
||||||
<div class="flex items-center gap-2 px-2 h-6">
|
<div class="flex items-center gap-2 px-2 h-6">
|
||||||
@@ -1912,6 +1937,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
onSelect={local.agent.set}
|
onSelect={local.agent.set}
|
||||||
class="capitalize"
|
class="capitalize"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
|
gutter={12}
|
||||||
/>
|
/>
|
||||||
</TooltipKeybind>
|
</TooltipKeybind>
|
||||||
<Show
|
<Show
|
||||||
@@ -1922,12 +1948,17 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
title={language.t("command.model.choose")}
|
title={language.t("command.model.choose")}
|
||||||
keybind={command.keybind("model.choose")}
|
keybind={command.keybind("model.choose")}
|
||||||
>
|
>
|
||||||
<Button as="div" variant="ghost" onClick={() => dialog.show(() => <DialogSelectModelUnpaid />)}>
|
<Button
|
||||||
|
as="div"
|
||||||
|
variant="ghost"
|
||||||
|
class="px-2"
|
||||||
|
onClick={() => dialog.show(() => <DialogSelectModelUnpaid />)}
|
||||||
|
>
|
||||||
<Show when={local.model.current()?.provider?.id}>
|
<Show when={local.model.current()?.provider?.id}>
|
||||||
<ProviderIcon id={local.model.current()!.provider.id as IconName} class="size-4 shrink-0" />
|
<ProviderIcon id={local.model.current()!.provider.id as IconName} class="size-4 shrink-0" />
|
||||||
</Show>
|
</Show>
|
||||||
{local.model.current()?.name ?? language.t("dialog.model.select.title")}
|
{local.model.current()?.name ?? language.t("dialog.model.select.title")}
|
||||||
<Icon name="chevron-down" size="small" />
|
<MorphChevron expanded={!!dialog.active?.id && dialog.active.id.startsWith("select-model-unpaid")} />
|
||||||
</Button>
|
</Button>
|
||||||
</TooltipKeybind>
|
</TooltipKeybind>
|
||||||
}
|
}
|
||||||
@@ -1937,12 +1968,16 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
title={language.t("command.model.choose")}
|
title={language.t("command.model.choose")}
|
||||||
keybind={command.keybind("model.choose")}
|
keybind={command.keybind("model.choose")}
|
||||||
>
|
>
|
||||||
<ModelSelectorPopover triggerAs={Button} triggerProps={{ variant: "ghost" }}>
|
<ModelSelectorPopover triggerAs={Button} triggerProps={{ variant: "ghost" }} gutter={12}>
|
||||||
|
{(open) => (
|
||||||
|
<>
|
||||||
<Show when={local.model.current()?.provider?.id}>
|
<Show when={local.model.current()?.provider?.id}>
|
||||||
<ProviderIcon id={local.model.current()!.provider.id as IconName} class="size-4 shrink-0" />
|
<ProviderIcon id={local.model.current()!.provider.id as IconName} class="size-4 shrink-0" />
|
||||||
</Show>
|
</Show>
|
||||||
{local.model.current()?.name ?? language.t("dialog.model.select.title")}
|
{local.model.current()?.name ?? language.t("dialog.model.select.title")}
|
||||||
<Icon name="chevron-down" size="small" />
|
<MorphChevron expanded={open} class="text-text-weak" />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</ModelSelectorPopover>
|
</ModelSelectorPopover>
|
||||||
</TooltipKeybind>
|
</TooltipKeybind>
|
||||||
</Show>
|
</Show>
|
||||||
@@ -1955,10 +1990,13 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
<Button
|
<Button
|
||||||
data-action="model-variant-cycle"
|
data-action="model-variant-cycle"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
class="text-text-base _hidden group-hover/prompt-input:inline-block capitalize text-12-regular"
|
class="text-text-strong text-12-regular"
|
||||||
onClick={() => local.model.variant.cycle()}
|
onClick={() => local.model.variant.cycle()}
|
||||||
>
|
>
|
||||||
{local.model.variant.current() ?? language.t("common.default")}
|
<Show when={local.model.variant.list().length > 1}>
|
||||||
|
<ReasoningIcon percentage={reasoningPercentage()} size={16} strokeWidth={1.25} />
|
||||||
|
</Show>
|
||||||
|
<CycleLabel value={currrentModelVariant()} />
|
||||||
</Button>
|
</Button>
|
||||||
</TooltipKeybind>
|
</TooltipKeybind>
|
||||||
</Show>
|
</Show>
|
||||||
@@ -1972,7 +2010,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
variant="ghost"
|
variant="ghost"
|
||||||
onClick={() => permission.toggleAutoAccept(params.id!, sdk.directory)}
|
onClick={() => permission.toggleAutoAccept(params.id!, sdk.directory)}
|
||||||
classList={{
|
classList={{
|
||||||
"_hidden group-hover/prompt-input:flex size-6 items-center justify-center": true,
|
"_hidden group-hover/prompt-input:flex items-center justify-center": true,
|
||||||
"text-text-base": !permission.isAutoAccepting(params.id!, sdk.directory),
|
"text-text-base": !permission.isAutoAccepting(params.id!, sdk.directory),
|
||||||
"hover:bg-surface-success-base": permission.isAutoAccepting(params.id!, sdk.directory),
|
"hover:bg-surface-success-base": permission.isAutoAccepting(params.id!, sdk.directory),
|
||||||
}}
|
}}
|
||||||
@@ -1994,7 +2032,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
</Match>
|
</Match>
|
||||||
</Switch>
|
</Switch>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center gap-3 absolute right-3 bottom-3">
|
<div class="flex items-center gap-1 absolute right-3 bottom-3">
|
||||||
<input
|
<input
|
||||||
ref={fileInputRef}
|
ref={fileInputRef}
|
||||||
type="file"
|
type="file"
|
||||||
@@ -2006,18 +2044,19 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
e.currentTarget.value = ""
|
e.currentTarget.value = ""
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-1.5 mr-1.5">
|
||||||
<SessionContextUsage />
|
<SessionContextUsage />
|
||||||
<Show when={store.mode === "normal"}>
|
<Show when={store.mode === "normal"}>
|
||||||
<Tooltip placement="top" value={language.t("prompt.action.attachFile")}>
|
<Tooltip placement="top" value={language.t("prompt.action.attachFile")}>
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
class="size-6"
|
size="small"
|
||||||
|
class="px-1"
|
||||||
onClick={() => fileInputRef.click()}
|
onClick={() => fileInputRef.click()}
|
||||||
aria-label={language.t("prompt.action.attachFile")}
|
aria-label={language.t("prompt.action.attachFile")}
|
||||||
>
|
>
|
||||||
<Icon name="photo" class="size-4.5" />
|
<Icon name="photo" class="size-6 text-icon-base" />
|
||||||
</Button>
|
</Button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</Show>
|
</Show>
|
||||||
@@ -2036,7 +2075,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
<Match when={true}>
|
<Match when={true}>
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<span>{language.t("prompt.action.send")}</span>
|
<span>{language.t("prompt.action.send")}</span>
|
||||||
<Icon name="enter" size="small" class="text-icon-base" />
|
<Icon name="enter" size="normal" class="text-icon-base" />
|
||||||
</div>
|
</div>
|
||||||
</Match>
|
</Match>
|
||||||
</Switch>
|
</Switch>
|
||||||
@@ -2047,7 +2086,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
disabled={!prompt.dirty() && !working()}
|
disabled={!prompt.dirty() && !working()}
|
||||||
icon={working() ? "stop" : "arrow-up"}
|
icon={working() ? "stop" : "arrow-up"}
|
||||||
variant="primary"
|
variant="primary"
|
||||||
class="h-6 w-4.5"
|
class="h-6 w-5.5"
|
||||||
aria-label={working() ? language.t("prompt.action.stop") : language.t("prompt.action.send")}
|
aria-label={working() ? language.t("prompt.action.stop") : language.t("prompt.action.send")}
|
||||||
/>
|
/>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
|||||||
@@ -80,13 +80,13 @@ const icons = {
|
|||||||
|
|
||||||
export interface IconProps extends ComponentProps<"svg"> {
|
export interface IconProps extends ComponentProps<"svg"> {
|
||||||
name: keyof typeof icons
|
name: keyof typeof icons
|
||||||
size?: "small" | "normal" | "medium" | "large"
|
size?: "small" | "normal" | "medium" | "large" | number
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Icon(props: IconProps) {
|
export function Icon(props: IconProps) {
|
||||||
const [local, others] = splitProps(props, ["name", "size", "class", "classList"])
|
const [local, others] = splitProps(props, ["name", "size", "class", "classList"])
|
||||||
return (
|
return (
|
||||||
<div data-component="icon" data-size={local.size || "normal"}>
|
<div data-component="icon" data-size={typeof local.size !== 'number' ? local.size || "normal" : `size-[${local.size}px]`}>
|
||||||
<svg
|
<svg
|
||||||
data-slot="icon-svg"
|
data-slot="icon-svg"
|
||||||
classList={{
|
classList={{
|
||||||
|
|||||||
Reference in New Issue
Block a user