summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorBrendan Allan <[email protected]>2026-04-24 14:04:55 +0800
committerGitHub <[email protected]>2026-04-24 14:04:55 +0800
commit4712f0f3c185fc3a348e2609689130fd2d901954 (patch)
tree7a05f5b581360ff959a6eb32708db2f81b80ddca
parent6c1268f3b18ed289bc524ed10add8c3caa6131d2 (diff)
downloadopencode-4712f0f3c185fc3a348e2609689130fd2d901954.tar.gz
opencode-4712f0f3c185fc3a348e2609689130fd2d901954.zip
feat(prompt): add shell mode UI with cancel button, custom icon, and example placeholder (#24105)
-rw-r--r--packages/app/src/components/prompt-input.tsx27
-rw-r--r--packages/app/src/components/prompt-input/placeholder.ts2
-rw-r--r--packages/app/src/i18n/ar.ts2
-rw-r--r--packages/app/src/i18n/br.ts2
-rw-r--r--packages/app/src/i18n/bs.ts2
-rw-r--r--packages/app/src/i18n/da.ts2
-rw-r--r--packages/app/src/i18n/de.ts2
-rw-r--r--packages/app/src/i18n/en.ts2
-rw-r--r--packages/app/src/i18n/es.ts2
-rw-r--r--packages/app/src/i18n/fr.ts2
-rw-r--r--packages/app/src/i18n/ja.ts2
-rw-r--r--packages/app/src/i18n/ko.ts2
-rw-r--r--packages/app/src/i18n/no.ts2
-rw-r--r--packages/app/src/i18n/pl.ts2
-rw-r--r--packages/app/src/i18n/ru.ts2
-rw-r--r--packages/app/src/i18n/th.ts2
-rw-r--r--packages/app/src/i18n/tr.ts2
-rw-r--r--packages/app/src/i18n/zh.ts2
-rw-r--r--packages/app/src/i18n/zht.ts2
-rw-r--r--packages/storybook/.storybook/mocks/app/context/language.ts2
-rw-r--r--packages/ui/src/components/icon.tsx4
21 files changed, 40 insertions, 29 deletions
diff --git a/packages/app/src/components/prompt-input.tsx b/packages/app/src/components/prompt-input.tsx
index 06c91c292..8b69f3b2a 100644
--- a/packages/app/src/components/prompt-input.tsx
+++ b/packages/app/src/components/prompt-input.tsx
@@ -270,7 +270,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
const buttonsSpring = useSpring(() => (store.mode === "normal" ? 1 : 0), { visualDuration: 0.2, bounce: 0 })
const motion = (value: number) => ({
opacity: value,
- transform: `scale(${0.95 + value * 0.05})`,
+ transform: `scale(${0.98 + value * 0.02})`,
filter: `blur(${(1 - value) * 2}px)`,
"pointer-events": value > 0.5 ? ("auto" as const) : ("none" as const),
})
@@ -345,7 +345,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
promptPlaceholder({
mode: store.mode,
commentCount: commentCount(),
- example: suggest() ? language.t(EXAMPLES[store.placeholder]) : "",
+ example: suggest() ? (store.mode === "shell" ? "git status" : language.t(EXAMPLES[store.placeholder])) : "",
suggest: suggest(),
t: (key, params) => language.t(key as Parameters<typeof language.t>[0], params as never),
}),
@@ -1403,12 +1403,11 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
<IconButton
data-action="prompt-submit"
type="submit"
- disabled={store.mode !== "normal" || (!working() && blank())}
+ disabled={!working() && blank()}
tabIndex={store.mode === "normal" ? undefined : -1}
- icon={stopping() ? "stop" : "arrow-up"}
+ icon={stopping() ? "stop" : store.mode === "shell" ? "arrow-undo-down" : "arrow-up"}
variant="primary"
class="size-8"
- style={buttons()}
aria-label={stopping() ? language.t("prompt.action.stop") : language.t("prompt.action.send")}
/>
</Tooltip>
@@ -1451,14 +1450,24 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
<div class="px-1.75 pt-5.5 pb-2 flex items-center gap-2 min-w-0">
<div class="flex items-center gap-1.5 min-w-0 flex-1 relative">
<div
- class="h-7 flex items-center gap-1.5 max-w-[160px] min-w-0 absolute inset-y-0 left-0"
+ class="h-7 flex items-center gap-1.5 min-w-0 absolute inset-0"
style={{
- padding: "0 4px 0 8px",
+ padding: "0 0px 0 8px",
...shell(),
}}
>
- <span class="truncate text-13-medium text-text-strong">{language.t("prompt.mode.shell")}</span>
- <div class="size-4 shrink-0" />
+ <Icon name="console" />
+ <span class="truncate text-13-medium text-text-base">{language.t("prompt.mode.shell")}</span>
+ <div class="flex-1" />
+ <Button
+ variant="ghost"
+ class="text-text-base"
+ onClick={() => {
+ setStore("mode", "normal")
+ }}
+ >
+ {language.t("common.cancel")}
+ </Button>
</div>
<div class="flex items-center gap-1.5 min-w-0 flex-1 h-7">
<Show when={!agentsLoading()}>
diff --git a/packages/app/src/components/prompt-input/placeholder.ts b/packages/app/src/components/prompt-input/placeholder.ts
index 395fee51b..6669f1361 100644
--- a/packages/app/src/components/prompt-input/placeholder.ts
+++ b/packages/app/src/components/prompt-input/placeholder.ts
@@ -7,7 +7,7 @@ type PromptPlaceholderInput = {
}
export function promptPlaceholder(input: PromptPlaceholderInput) {
- if (input.mode === "shell") return input.t("prompt.placeholder.shell")
+ if (input.mode === "shell") return input.t("prompt.placeholder.shell", { example: input.example })
if (input.commentCount > 1) return input.t("prompt.placeholder.summarizeComments")
if (input.commentCount === 1) return input.t("prompt.placeholder.summarizeComment")
if (!input.suggest) return input.t("prompt.placeholder.simple")
diff --git a/packages/app/src/i18n/ar.ts b/packages/app/src/i18n/ar.ts
index efb2919a5..702210e4d 100644
--- a/packages/app/src/i18n/ar.ts
+++ b/packages/app/src/i18n/ar.ts
@@ -210,7 +210,7 @@ export const dict = {
"common.saving": "جارٍ الحفظ...",
"common.default": "افتراضي",
"common.attachment": "مرفق",
- "prompt.placeholder.shell": "أدخل أمر shell...",
+ "prompt.placeholder.shell": "أدخل أمر shell... {{example}}",
"prompt.placeholder.normal": 'اسأل أي شيء... "{{example}}"',
"prompt.placeholder.simple": "اسأل أي شيء...",
"prompt.placeholder.summarizeComments": "لخّص التعليقات…",
diff --git a/packages/app/src/i18n/br.ts b/packages/app/src/i18n/br.ts
index 022d01298..b414fff36 100644
--- a/packages/app/src/i18n/br.ts
+++ b/packages/app/src/i18n/br.ts
@@ -210,7 +210,7 @@ export const dict = {
"common.saving": "Salvando...",
"common.default": "Padrão",
"common.attachment": "anexo",
- "prompt.placeholder.shell": "Digite comando do shell...",
+ "prompt.placeholder.shell": "Digite comando do shell... {{example}}",
"prompt.placeholder.normal": 'Pergunte qualquer coisa... "{{example}}"',
"prompt.placeholder.simple": "Pergunte qualquer coisa...",
"prompt.placeholder.summarizeComments": "Resumir comentários…",
diff --git a/packages/app/src/i18n/bs.ts b/packages/app/src/i18n/bs.ts
index 15d8376ab..e316f87da 100644
--- a/packages/app/src/i18n/bs.ts
+++ b/packages/app/src/i18n/bs.ts
@@ -228,7 +228,7 @@ export const dict = {
"common.default": "Podrazumijevano",
"common.attachment": "prilog",
- "prompt.placeholder.shell": "Unesi shell naredbu...",
+ "prompt.placeholder.shell": "Unesi shell naredbu... {{example}}",
"prompt.placeholder.normal": 'Pitaj bilo šta... "{{example}}"',
"prompt.placeholder.simple": "Pitaj bilo šta...",
"prompt.placeholder.summarizeComments": "Sažmi komentare…",
diff --git a/packages/app/src/i18n/da.ts b/packages/app/src/i18n/da.ts
index 03cfe2b78..d368f292d 100644
--- a/packages/app/src/i18n/da.ts
+++ b/packages/app/src/i18n/da.ts
@@ -226,7 +226,7 @@ export const dict = {
"common.default": "Standard",
"common.attachment": "vedhæftning",
- "prompt.placeholder.shell": "Indtast shell-kommando...",
+ "prompt.placeholder.shell": "Indtast shell-kommando... {{example}}",
"prompt.placeholder.normal": 'Spørg om hvad som helst... "{{example}}"',
"prompt.placeholder.simple": "Spørg om hvad som helst...",
"prompt.placeholder.summarizeComments": "Opsummér kommentarer…",
diff --git a/packages/app/src/i18n/de.ts b/packages/app/src/i18n/de.ts
index ccb88e9f4..a2b049c88 100644
--- a/packages/app/src/i18n/de.ts
+++ b/packages/app/src/i18n/de.ts
@@ -215,7 +215,7 @@ export const dict = {
"common.saving": "Speichert...",
"common.default": "Standard",
"common.attachment": "Anhang",
- "prompt.placeholder.shell": "Shell-Befehl eingeben...",
+ "prompt.placeholder.shell": "Shell-Befehl eingeben... {{example}}",
"prompt.placeholder.normal": 'Fragen Sie alles... "{{example}}"',
"prompt.placeholder.simple": "Fragen Sie alles...",
"prompt.placeholder.summarizeComments": "Kommentare zusammenfassen…",
diff --git a/packages/app/src/i18n/en.ts b/packages/app/src/i18n/en.ts
index ed80b38ce..7326f7c8b 100644
--- a/packages/app/src/i18n/en.ts
+++ b/packages/app/src/i18n/en.ts
@@ -230,7 +230,7 @@ export const dict = {
"common.default": "Default",
"common.attachment": "attachment",
- "prompt.placeholder.shell": "Enter shell command...",
+ "prompt.placeholder.shell": "Enter shell command... {{example}}",
"prompt.placeholder.normal": 'Ask anything... "{{example}}"',
"prompt.placeholder.simple": "Ask anything...",
"prompt.placeholder.summarizeComments": "Summarize comments…",
diff --git a/packages/app/src/i18n/es.ts b/packages/app/src/i18n/es.ts
index 0b4789c2a..8dc644bb8 100644
--- a/packages/app/src/i18n/es.ts
+++ b/packages/app/src/i18n/es.ts
@@ -227,7 +227,7 @@ export const dict = {
"common.default": "Predeterminado",
"common.attachment": "adjunto",
- "prompt.placeholder.shell": "Introduce comando de shell...",
+ "prompt.placeholder.shell": "Introduce comando de shell... {{example}}",
"prompt.placeholder.normal": 'Pregunta cualquier cosa... "{{example}}"',
"prompt.placeholder.simple": "Pregunta cualquier cosa...",
"prompt.placeholder.summarizeComments": "Resumir comentarios…",
diff --git a/packages/app/src/i18n/fr.ts b/packages/app/src/i18n/fr.ts
index 4d73f626b..1b4916c7d 100644
--- a/packages/app/src/i18n/fr.ts
+++ b/packages/app/src/i18n/fr.ts
@@ -210,7 +210,7 @@ export const dict = {
"common.saving": "Enregistrement...",
"common.default": "Défaut",
"common.attachment": "pièce jointe",
- "prompt.placeholder.shell": "Entrez une commande shell...",
+ "prompt.placeholder.shell": "Entrez une commande shell... {{example}}",
"prompt.placeholder.normal": 'Demandez n\'importe quoi... "{{example}}"',
"prompt.placeholder.simple": "Demandez n'importe quoi...",
"prompt.placeholder.summarizeComments": "Résumer les commentaires…",
diff --git a/packages/app/src/i18n/ja.ts b/packages/app/src/i18n/ja.ts
index 493b1f17f..979f94203 100644
--- a/packages/app/src/i18n/ja.ts
+++ b/packages/app/src/i18n/ja.ts
@@ -209,7 +209,7 @@ export const dict = {
"common.saving": "保存中...",
"common.default": "デフォルト",
"common.attachment": "添付ファイル",
- "prompt.placeholder.shell": "シェルコマンドを入力...",
+ "prompt.placeholder.shell": "シェルコマンドを入力... {{example}}",
"prompt.placeholder.normal": '何でも聞いてください... "{{example}}"',
"prompt.placeholder.simple": "何でも聞いてください...",
"prompt.placeholder.summarizeComments": "コメントを要約…",
diff --git a/packages/app/src/i18n/ko.ts b/packages/app/src/i18n/ko.ts
index 0218cc1a9..56ce374a9 100644
--- a/packages/app/src/i18n/ko.ts
+++ b/packages/app/src/i18n/ko.ts
@@ -209,7 +209,7 @@ export const dict = {
"common.saving": "저장 중...",
"common.default": "기본값",
"common.attachment": "첨부 파일",
- "prompt.placeholder.shell": "셸 명령어 입력...",
+ "prompt.placeholder.shell": "셸 명령어 입력... {{example}}",
"prompt.placeholder.normal": '무엇이든 물어보세요... "{{example}}"',
"prompt.placeholder.simple": "무엇이든 물어보세요...",
"prompt.placeholder.summarizeComments": "댓글 요약…",
diff --git a/packages/app/src/i18n/no.ts b/packages/app/src/i18n/no.ts
index 43aa84420..d14dd6f98 100644
--- a/packages/app/src/i18n/no.ts
+++ b/packages/app/src/i18n/no.ts
@@ -230,7 +230,7 @@ export const dict = {
"common.default": "Standard",
"common.attachment": "vedlegg",
- "prompt.placeholder.shell": "Skriv inn shell-kommando...",
+ "prompt.placeholder.shell": "Skriv inn shell-kommando... {{example}}",
"prompt.placeholder.normal": 'Spør om hva som helst... "{{example}}"',
"prompt.placeholder.simple": "Spør om hva som helst...",
"prompt.placeholder.summarizeComments": "Oppsummer kommentarer…",
diff --git a/packages/app/src/i18n/pl.ts b/packages/app/src/i18n/pl.ts
index 6c6d4dddc..9859ea0ae 100644
--- a/packages/app/src/i18n/pl.ts
+++ b/packages/app/src/i18n/pl.ts
@@ -211,7 +211,7 @@ export const dict = {
"common.saving": "Zapisywanie...",
"common.default": "Domyślny",
"common.attachment": "załącznik",
- "prompt.placeholder.shell": "Wpisz polecenie terminala...",
+ "prompt.placeholder.shell": "Wpisz polecenie terminala... {{example}}",
"prompt.placeholder.normal": 'Zapytaj o cokolwiek... "{{example}}"',
"prompt.placeholder.simple": "Zapytaj o cokolwiek...",
"prompt.placeholder.summarizeComments": "Podsumuj komentarze…",
diff --git a/packages/app/src/i18n/ru.ts b/packages/app/src/i18n/ru.ts
index e0b094877..6e6ca3203 100644
--- a/packages/app/src/i18n/ru.ts
+++ b/packages/app/src/i18n/ru.ts
@@ -227,7 +227,7 @@ export const dict = {
"common.default": "По умолчанию",
"common.attachment": "вложение",
- "prompt.placeholder.shell": "Введите команду оболочки...",
+ "prompt.placeholder.shell": "Введите команду оболочки... {{example}}",
"prompt.placeholder.normal": 'Спросите что угодно... "{{example}}"',
"prompt.placeholder.simple": "Спросите что угодно...",
"prompt.placeholder.summarizeComments": "Суммировать комментарии…",
diff --git a/packages/app/src/i18n/th.ts b/packages/app/src/i18n/th.ts
index 8a15f29c0..84e5d3ff2 100644
--- a/packages/app/src/i18n/th.ts
+++ b/packages/app/src/i18n/th.ts
@@ -227,7 +227,7 @@ export const dict = {
"common.default": "ค่าเริ่มต้น",
"common.attachment": "ไฟล์แนบ",
- "prompt.placeholder.shell": "ป้อนคำสั่งเชลล์...",
+ "prompt.placeholder.shell": "ป้อนคำสั่งเชลล์... {{example}}",
"prompt.placeholder.normal": 'ถามอะไรก็ได้... "{{example}}"',
"prompt.placeholder.simple": "ถามอะไรก็ได้...",
"prompt.placeholder.summarizeComments": "สรุปความคิดเห็น…",
diff --git a/packages/app/src/i18n/tr.ts b/packages/app/src/i18n/tr.ts
index f20c05000..06e233cb5 100644
--- a/packages/app/src/i18n/tr.ts
+++ b/packages/app/src/i18n/tr.ts
@@ -232,7 +232,7 @@ export const dict = {
"common.default": "Varsayılan",
"common.attachment": "ek",
- "prompt.placeholder.shell": "Kabuk komutu girin...",
+ "prompt.placeholder.shell": "Kabuk komutu girin... {{example}}",
"prompt.placeholder.normal": 'Bir şeyler sorun... "{{example}}"',
"prompt.placeholder.simple": "Bir şeyler sorun...",
"prompt.placeholder.summarizeComments": "Yorumları özetle…",
diff --git a/packages/app/src/i18n/zh.ts b/packages/app/src/i18n/zh.ts
index 05310df96..fa83707e8 100644
--- a/packages/app/src/i18n/zh.ts
+++ b/packages/app/src/i18n/zh.ts
@@ -249,7 +249,7 @@ export const dict = {
"common.default": "默认",
"common.attachment": "附件",
- "prompt.placeholder.shell": "输入 shell 命令...",
+ "prompt.placeholder.shell": "输入 shell 命令... {{example}}",
"prompt.placeholder.normal": '随便问点什么... "{{example}}"',
"prompt.placeholder.simple": "随便问点什么...",
"prompt.placeholder.summarizeComments": "总结评论…",
diff --git a/packages/app/src/i18n/zht.ts b/packages/app/src/i18n/zht.ts
index 43681c779..e9d265acc 100644
--- a/packages/app/src/i18n/zht.ts
+++ b/packages/app/src/i18n/zht.ts
@@ -227,7 +227,7 @@ export const dict = {
"common.default": "預設",
"common.attachment": "附件",
- "prompt.placeholder.shell": "輸入 shell 命令...",
+ "prompt.placeholder.shell": "輸入 shell 命令... {{example}}",
"prompt.placeholder.normal": '隨便問點什麼... "{{example}}"',
"prompt.placeholder.simple": "隨便問點什麼...",
"prompt.placeholder.summarizeComments": "摘要評論…",
diff --git a/packages/storybook/.storybook/mocks/app/context/language.ts b/packages/storybook/.storybook/mocks/app/context/language.ts
index c3317ca2e..df28d79fb 100644
--- a/packages/storybook/.storybook/mocks/app/context/language.ts
+++ b/packages/storybook/.storybook/mocks/app/context/language.ts
@@ -5,7 +5,7 @@ const dict: Record<string, string> = {
"prompt.loading": "Loading prompt...",
"prompt.placeholder.normal": "Ask anything...",
"prompt.placeholder.simple": "Ask anything...",
- "prompt.placeholder.shell": "Run a shell command...",
+ "prompt.placeholder.shell": "Run a shell command... {{example}}",
"prompt.placeholder.summarizeComment": "Summarize this comment",
"prompt.placeholder.summarizeComments": "Summarize these comments",
"prompt.action.attachFile": "Attach files",
diff --git a/packages/ui/src/components/icon.tsx b/packages/ui/src/components/icon.tsx
index 08726d0ff..2e4d1f53b 100644
--- a/packages/ui/src/components/icon.tsx
+++ b/packages/ui/src/components/icon.tsx
@@ -102,6 +102,7 @@ const icons = {
link: `<path d="M2.08334 12.0833L1.72979 11.7298L1.37624 12.0833L1.72979 12.4369L2.08334 12.0833ZM7.91668 17.9167L7.56312 18.2702L7.91668 18.6238L8.27023 18.2702L7.91668 17.9167ZM17.9167 7.91666L18.2702 8.27022L18.6238 7.91666L18.2702 7.56311L17.9167 7.91666ZM12.0833 2.08333L12.4369 1.72977L12.0833 1.37622L11.7298 1.72977L12.0833 2.08333ZM8.39646 5.06311L8.0429 5.41666L8.75001 6.12377L9.10356 5.77021L8.75001 5.41666L8.39646 5.06311ZM5.77023 9.10355L6.12378 8.74999L5.41668 8.04289L5.06312 8.39644L5.41668 8.74999L5.77023 9.10355ZM14.2298 10.8964L13.8762 11.25L14.5833 11.9571L14.9369 11.6035L14.5833 11.25L14.2298 10.8964ZM11.6036 14.9369L11.9571 14.5833L11.25 13.8762L10.8965 14.2298L11.25 14.5833L11.6036 14.9369ZM7.14646 12.1464L6.7929 12.5L7.50001 13.2071L7.85356 12.8535L7.50001 12.5L7.14646 12.1464ZM12.8536 7.85355L13.2071 7.49999L12.5 6.79289L12.1465 7.14644L12.5 7.49999L12.8536 7.85355ZM2.08334 12.0833L1.72979 12.4369L7.56312 18.2702L7.91668 17.9167L8.27023 17.5631L2.4369 11.7298L2.08334 12.0833ZM17.9167 7.91666L18.2702 7.56311L12.4369 1.72977L12.0833 2.08333L11.7298 2.43688L17.5631 8.27022L17.9167 7.91666ZM12.0833 2.08333L11.7298 1.72977L8.39646 5.06311L8.75001 5.41666L9.10356 5.77021L12.4369 2.43688L12.0833 2.08333ZM5.41668 8.74999L5.06312 8.39644L1.72979 11.7298L2.08334 12.0833L2.4369 12.4369L5.77023 9.10355L5.41668 8.74999ZM14.5833 11.25L14.9369 11.6035L18.2702 8.27022L17.9167 7.91666L17.5631 7.56311L14.2298 10.8964L14.5833 11.25ZM7.91668 17.9167L8.27023 18.2702L11.6036 14.9369L11.25 14.5833L10.8965 14.2298L7.56312 17.5631L7.91668 17.9167ZM7.50001 12.5L7.85356 12.8535L12.8536 7.85355L12.5 7.49999L12.1465 7.14644L7.14646 12.1464L7.50001 12.5Z" fill="currentColor"/>`,
providers: `<path d="M10.0001 4.37562V2.875M13 4.37793V2.87793M7.00014 4.37793V2.875M10 17.1279V15.6279M13 17.1279V15.6279M7 17.1279V15.6279M15.625 13.0029H17.125M15.625 7.00293H17.125M15.625 10.0029H17.125M2.875 10.0029H4.375M2.875 13.0029H4.375M2.875 7.00293H4.375M4.375 4.37793H15.625V15.6279H4.375V4.37793ZM12.6241 10.0022C12.6241 11.4519 11.4488 12.6272 9.99908 12.6272C8.54934 12.6272 7.37408 11.4519 7.37408 10.0022C7.37408 8.55245 8.54934 7.3772 9.99908 7.3772C11.4488 7.3772 12.6241 8.55245 12.6241 10.0022Z" stroke="currentColor" stroke-linecap="square"/>`,
models: `<path fill-rule="evenodd" clip-rule="evenodd" d="M17.5 10C12.2917 10 10 12.2917 10 17.5C10 12.2917 7.70833 10 2.5 10C7.70833 10 10 7.70833 10 2.5C10 7.70833 12.2917 10 17.5 10Z" stroke="currentColor"/>`,
+ "arrow-undo-down": `<path d="M4.08333 11.0859L1.75 8.7526L4.08333 6.41927M2.33333 8.7526L12.5417 8.7526L12.5417 3.21094L7 3.21094" stroke="currentColor" stroke-width="1" stroke-linecap="square"/>`,
}
export interface IconProps extends ComponentProps<"svg"> {
@@ -111,7 +112,8 @@ export interface IconProps extends ComponentProps<"svg"> {
export function Icon(props: IconProps) {
const [local, others] = splitProps(props, ["name", "size", "class", "classList"])
- const viewBox = () => (local.name === "magnifying-glass" ? "0 0 16 16" : "0 0 20 20")
+ const viewBox = () =>
+ local.name === "magnifying-glass" || local.name === "arrow-undo-down" ? "0 0 16 16" : "0 0 20 20"
return (
<div data-component="icon" data-size={local.size || "normal"}>
<svg