Compare commits

...
2 changed files with 184 additions and 110 deletions
@@ -3,6 +3,7 @@ import { createStore } from "solid-js/store"
import { Button } from "@opencode-ai/ui/button" import { Button } from "@opencode-ai/ui/button"
import { DockPrompt } from "@opencode-ai/ui/dock-prompt" import { DockPrompt } from "@opencode-ai/ui/dock-prompt"
import { Icon } from "@opencode-ai/ui/icon" import { Icon } from "@opencode-ai/ui/icon"
import { IconButton } from "@opencode-ai/ui/icon-button"
import { showToast } from "@opencode-ai/ui/toast" import { showToast } from "@opencode-ai/ui/toast"
import type { QuestionAnswer, QuestionRequest } from "@opencode-ai/sdk/v2" import type { QuestionAnswer, QuestionRequest } from "@opencode-ai/sdk/v2"
import { useLanguage } from "@/context/language" import { useLanguage } from "@/context/language"
@@ -22,6 +23,7 @@ export const SessionQuestionDock: Component<{ request: QuestionRequest; onSubmit
customOn: [] as boolean[], customOn: [] as boolean[],
editing: false, editing: false,
sending: false, sending: false,
collapsed: false,
}) })
let root: HTMLDivElement | undefined let root: HTMLDivElement | undefined
@@ -31,6 +33,7 @@ export const SessionQuestionDock: Component<{ request: QuestionRequest; onSubmit
const input = createMemo(() => store.custom[store.tab] ?? "") const input = createMemo(() => store.custom[store.tab] ?? "")
const on = createMemo(() => store.customOn[store.tab] === true) const on = createMemo(() => store.customOn[store.tab] === true)
const multi = createMemo(() => question()?.multiple === true) const multi = createMemo(() => question()?.multiple === true)
const picked = createMemo(() => store.answers[store.tab]?.length ?? 0)
const summary = createMemo(() => { const summary = createMemo(() => {
const n = Math.min(store.tab + 1, total()) const n = Math.min(store.tab + 1, total())
@@ -39,6 +42,8 @@ export const SessionQuestionDock: Component<{ request: QuestionRequest; onSubmit
const last = createMemo(() => store.tab >= total() - 1) const last = createMemo(() => store.tab >= total() - 1)
const fold = () => setStore("collapsed", (value) => !value)
const customUpdate = (value: string, selected: boolean = on()) => { const customUpdate = (value: string, selected: boolean = on()) => {
const prev = input().trim() const prev = input().trim()
const next = value.trim() const next = value.trim()
@@ -239,9 +244,21 @@ export const SessionQuestionDock: Component<{ request: QuestionRequest; onSubmit
kind="question" kind="question"
ref={(el) => (root = el)} ref={(el) => (root = el)}
header={ header={
<> <div
data-action="session-question-toggle"
class="flex flex-1 min-w-0 items-center gap-2 cursor-default select-none"
role="button"
tabIndex={0}
style={{ margin: "0 -10px", padding: "0 0 0 10px" }}
onClick={fold}
onKeyDown={(event) => {
if (event.key !== "Enter" && event.key !== " ") return
event.preventDefault()
fold()
}}
>
<div data-slot="question-header-title">{summary()}</div> <div data-slot="question-header-title">{summary()}</div>
<div data-slot="question-progress"> <div data-slot="question-progress" class="ml-auto mr-1">
<For each={questions()}> <For each={questions()}>
{(_, i) => ( {(_, i) => (
<button <button
@@ -253,13 +270,38 @@ export const SessionQuestionDock: Component<{ request: QuestionRequest; onSubmit
(store.customOn[i()] === true && (store.custom[i()] ?? "").trim().length > 0) (store.customOn[i()] === true && (store.custom[i()] ?? "").trim().length > 0)
} }
disabled={store.sending} disabled={store.sending}
onClick={() => jump(i())} onMouseDown={(event) => {
event.preventDefault()
event.stopPropagation()
}}
onClick={(event) => {
event.stopPropagation()
jump(i())
}}
aria-label={`${language.t("ui.tool.questions")} ${i() + 1}`} aria-label={`${language.t("ui.tool.questions")} ${i() + 1}`}
/> />
)} )}
</For> </For>
</div> </div>
</> <div>
<IconButton
data-action="session-question-toggle-button"
icon="chevron-down"
size="normal"
variant="ghost"
classList={{ "rotate-180": store.collapsed }}
onMouseDown={(event) => {
event.preventDefault()
event.stopPropagation()
}}
onClick={(event) => {
event.stopPropagation()
fold()
}}
aria-label={store.collapsed ? language.t("session.todo.expand") : language.t("session.todo.collapse")}
/>
</div>
</div>
} }
footer={ footer={
<> <>
@@ -279,7 +321,30 @@ export const SessionQuestionDock: Component<{ request: QuestionRequest; onSubmit
</> </>
} }
> >
<div data-slot="question-text">{question()?.question}</div> <div
data-slot="question-text"
class="cursor-default"
classList={{
"mb-6": store.collapsed && picked() === 0,
}}
role={store.collapsed ? "button" : undefined}
tabIndex={store.collapsed ? 0 : undefined}
onClick={fold}
onKeyDown={(event) => {
if (!store.collapsed) return
if (event.key !== "Enter" && event.key !== " ") return
event.preventDefault()
fold()
}}
>
{question()?.question}
</div>
<Show when={store.collapsed && picked() > 0}>
<div data-slot="question-hint" class="cursor-default mb-6">
{picked()} answer{picked() === 1 ? "" : "s"} selected
</div>
</Show>
<div data-slot="question-answers" hidden={store.collapsed} aria-hidden={store.collapsed}>
<Show when={multi()} fallback={<div data-slot="question-hint">{language.t("ui.question.singleHint")}</div>}> <Show when={multi()} fallback={<div data-slot="question-hint">{language.t("ui.question.singleHint")}</div>}>
<div data-slot="question-hint">{language.t("ui.question.multiHint")}</div> <div data-slot="question-hint">{language.t("ui.question.multiHint")}</div>
</Show> </Show>
@@ -422,6 +487,7 @@ export const SessionQuestionDock: Component<{ request: QuestionRequest; onSubmit
</form> </form>
</Show> </Show>
</div> </div>
</div>
</DockPrompt> </DockPrompt>
) )
} }
+10 -2
View File
@@ -785,7 +785,7 @@
[data-slot="question-body"] { [data-slot="question-body"] {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 16px; gap: 0;
flex: 1; flex: 1;
min-height: 0; min-height: 0;
padding: 8px 8px 0; padding: 8px 8px 0;
@@ -865,7 +865,7 @@
font-weight: var(--font-weight-medium); font-weight: var(--font-weight-medium);
line-height: var(--line-height-large); line-height: var(--line-height-large);
color: var(--text-strong); color: var(--text-strong);
padding: 0 10px; padding: 16px 10px 0;
} }
[data-slot="question-hint"] { [data-slot="question-hint"] {
@@ -1020,6 +1020,14 @@
white-space: normal; white-space: normal;
overflow-wrap: anywhere; overflow-wrap: anywhere;
} }
&[data-picked="true"] {
[data-slot="question-custom-input"]:focus-visible {
outline: none;
outline-offset: 0;
border-radius: 0;
}
}
} }
[data-slot="question-custom"] { [data-slot="question-custom"] {