diff --git a/packages/kilo-vscode/tests/visual-regression.spec.ts-snapshots/chat/question-dock-multi-chromium-linux.png b/packages/kilo-vscode/tests/visual-regression.spec.ts-snapshots/chat/question-dock-multi-chromium-linux.png index 45711758e1c..42623d20283 100644 --- a/packages/kilo-vscode/tests/visual-regression.spec.ts-snapshots/chat/question-dock-multi-chromium-linux.png +++ b/packages/kilo-vscode/tests/visual-regression.spec.ts-snapshots/chat/question-dock-multi-chromium-linux.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:550464711e0d3aab99fa514d6de9501be19e49b65d5643b7762fb7b5ca47464c -size 19715 +oid sha256:b8b1ba56d6632bfcbd0e4c5b0c1514cd80832339dde0c86568572ada656ca33d +size 24134 diff --git a/packages/kilo-vscode/tests/visual-regression.spec.ts-snapshots/chat/question-dock-single-chromium-linux.png b/packages/kilo-vscode/tests/visual-regression.spec.ts-snapshots/chat/question-dock-single-chromium-linux.png index 5f33984b45c..21f6afd99c2 100644 --- a/packages/kilo-vscode/tests/visual-regression.spec.ts-snapshots/chat/question-dock-single-chromium-linux.png +++ b/packages/kilo-vscode/tests/visual-regression.spec.ts-snapshots/chat/question-dock-single-chromium-linux.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2cdb43fe23e17f32dd96b36eb3b2f97b4b84c29983996f26406db6ead2f41785 -size 25707 +oid sha256:278855516fe11d95b9de942d9690531c114b0a0b9ac0239c2ef31f82b420bd7e +size 32467 diff --git a/packages/kilo-vscode/tests/visual-regression.spec.ts-snapshots/composite-webview/inline-question-chromium-linux.png b/packages/kilo-vscode/tests/visual-regression.spec.ts-snapshots/composite-webview/inline-question-chromium-linux.png index 78ab5e57fcb..d25eccfaf9e 100644 --- a/packages/kilo-vscode/tests/visual-regression.spec.ts-snapshots/composite-webview/inline-question-chromium-linux.png +++ b/packages/kilo-vscode/tests/visual-regression.spec.ts-snapshots/composite-webview/inline-question-chromium-linux.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:18bd63c206b2afc5d8d05e06a8a1113c7dfa1019e85eebe570be40419dbe35fd -size 28187 +oid sha256:f6193ba79b4d5dda29bf39d1127dacd7e332755d1f6f42dd67ff667088975659 +size 35678 diff --git a/packages/kilo-vscode/tests/visual-regression.spec.ts-snapshots/composite-webview/question-dismissed-chromium-linux.png b/packages/kilo-vscode/tests/visual-regression.spec.ts-snapshots/composite-webview/question-dismissed-chromium-linux.png new file mode 100644 index 00000000000..f7e30521e5a --- /dev/null +++ b/packages/kilo-vscode/tests/visual-regression.spec.ts-snapshots/composite-webview/question-dismissed-chromium-linux.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:607707e2201d1e50369a5897266136387b2444570b61ac2062c658335a0cdcc7 +size 5055 diff --git a/packages/kilo-vscode/webview-ui/src/components/chat/QuestionDock.tsx b/packages/kilo-vscode/webview-ui/src/components/chat/QuestionDock.tsx index a03a77c1eb6..ee97a5dcb10 100644 --- a/packages/kilo-vscode/webview-ui/src/components/chat/QuestionDock.tsx +++ b/packages/kilo-vscode/webview-ui/src/components/chat/QuestionDock.tsx @@ -1,12 +1,13 @@ /** * QuestionDock component * Displays question requests from the AI assistant inline above the prompt input. - * Uses kilo-ui's data-component/data-slot CSS pattern for styling. + * Uses kilo-ui's DockPrompt component for proper surface styling. */ import { Component, For, Show, createMemo, createEffect } from "solid-js" import { createStore } from "solid-js/store" import { Button } from "@kilocode/kilo-ui/button" +import { DockPrompt } from "@kilocode/kilo-ui/dock-prompt" import { Icon } from "@kilocode/kilo-ui/icon" import { useSession } from "../../context/session" import { useLanguage } from "../../context/language" @@ -148,10 +149,11 @@ export const QuestionDock: Component<{ request: QuestionRequest }> = (props) => } return ( -
e.stopPropagation()}> -
-
-
+
e.stopPropagation()}> +
{summary()}
@@ -177,62 +179,65 @@ export const QuestionDock: Component<{ request: QuestionRequest }> = (props) =>
-
- - -
-
{question()?.question}
- {language.t("ui.question.singleHint")}
} - > -
{language.t("ui.question.multiHint")}
+ + } + footer={ + <> + +
+ 0}> + -
- - {(opt, i) => { - const picked = () => store.answers[store.tab]?.includes(opt.label) ?? false - return ( - - ) - }} - - + selectTab(store.tab + 1)} + disabled={store.sending || (!confirm() && (store.answers[store.tab]?.length ?? 0) === 0)} + > + {last() && single() + ? language.t("ui.common.submit") + : last() + ? language.t("common.review") + : language.t("ui.common.next")} + + } + > + + +
+ + } + > + +
{question()?.question}
+ {language.t("ui.question.singleHint")}
}> +
{language.t("ui.question.multiHint")}
+
+
+ + {(opt, i) => { + const picked = () => store.answers[store.tab]?.includes(opt.label) ?? false + return ( - -
- setTimeout(() => el.focus(), 0)} - type="text" - data-slot="custom-input" - placeholder={language.t("ui.question.custom.placeholder")} - value={input()} - disabled={store.sending} - onInput={(e) => { - const inputs = [...store.custom] - inputs[store.tab] = e.currentTarget.value - setStore("custom", inputs) - }} - /> - - -
-
- -
-
- - - -
-
{language.t("ui.messagePart.review.title")}
- - {(q, index) => { - const value = () => store.answers[index()]?.join(", ") ?? "" - const answered = () => Boolean(value()) - return ( -
- {q.question} - - {answered() ? value() : language.t("ui.question.review.notAnswered")} - -
- ) - }} -
-
-
-
- -
- -
- 0}> - - - selectTab(store.tab + 1)} - disabled={store.sending || (!confirm() && (store.answers[store.tab]?.length ?? 0) === 0)} - > - {last() && single() - ? language.t("ui.common.submit") - : last() - ? language.t("common.review") - : language.t("ui.common.next")} - - } - > - + ) + }} + + + + +
+ setTimeout(() => el.focus(), 0)} + type="text" + data-slot="custom-input" + placeholder={language.t("ui.question.custom.placeholder")} + value={input()} + disabled={store.sending} + onInput={(e) => { + const inputs = [...store.custom] + inputs[store.tab] = e.currentTarget.value + setStore("custom", inputs) + }} + /> + + +
+
-
-
+ + + +
+
{language.t("ui.messagePart.review.title")}
+ + {(q, index) => { + const value = () => store.answers[index()]?.join(", ") ?? "" + const answered = () => Boolean(value()) + return ( +
+ {q.question} + + {answered() ? value() : language.t("ui.question.review.notAnswered")} + +
+ ) + }} +
+
+
+
) } diff --git a/packages/kilo-vscode/webview-ui/src/stories/composite.stories.tsx b/packages/kilo-vscode/webview-ui/src/stories/composite.stories.tsx index 9b7c98aec81..aab5bac37ff 100644 --- a/packages/kilo-vscode/webview-ui/src/stories/composite.stories.tsx +++ b/packages/kilo-vscode/webview-ui/src/stories/composite.stories.tsx @@ -231,6 +231,23 @@ const questionToolPart: ToolPart = { }, } +const questionDismissedPart: ToolPart = { + id: "part-question-dismissed-001", + sessionID: SESSION_ID, + messageID: ASST_MSG_ID, + type: "tool", + callID: "call-question-dismissed-001", + tool: "question", + state: { + status: "error", + input: { question: "Which testing framework?", options: [] }, + error: "Error: User dismissed this question", + title: "Question dismissed", + metadata: {}, + time: { start: now - 2000, end: now - 1500 }, + }, +} + // --------------------------------------------------------------------------- // Data helpers // --------------------------------------------------------------------------- @@ -457,3 +474,19 @@ export const InlineQuestion: Story = { ) }, } + +// --------------------------------------------------------------------------- +// 9. Dismissed question (right-aligned "Questions dismissed" text) +// --------------------------------------------------------------------------- + +export const QuestionDismissed: Story = { + name: "Question Dismissed", + render: () => { + const data = dataWith([textPart, questionDismissedPart]) + return ( + + + + ) + }, +} diff --git a/packages/kilo-vscode/webview-ui/src/styles/chat.css b/packages/kilo-vscode/webview-ui/src/styles/chat.css index 90c9774b067..ee9dca5d81e 100644 --- a/packages/kilo-vscode/webview-ui/src/styles/chat.css +++ b/packages/kilo-vscode/webview-ui/src/styles/chat.css @@ -912,6 +912,10 @@ padding: 0 4px; } +.vscode-session-turn + .vscode-session-turn { + margin-top: 12px; +} + .vscode-session-turn-user { width: 100%; } @@ -937,14 +941,10 @@ } /* ============================================ - Question Prompt + Question Prompt (VS Code overrides) ============================================ */ -[data-component="question-prompt"] { - [data-slot="question-header"] { - padding: 0; - } - +[data-component="dock-prompt"][data-kind="question"] { [data-slot="question-progress-nav"] { display: inline-flex; align-items: center; @@ -972,12 +972,6 @@ } } - [data-slot="question-footer"] { - padding: 8px; - padding-top: 8px; - margin-top: 0; - } - [data-slot="question-footer"] > [data-component="button"][data-variant="ghost"] { border: none; box-shadow: none; @@ -997,155 +991,11 @@ color: var(--text-weaker); } - [data-slot="question-text"] { - font-size: var(--font-size-base); - padding: 0 2px; - } - - [data-slot="question-hint"] { - font-size: var(--font-size-small); - padding: 0 2px; - } - - [data-slot="question-options"] { - margin-top: 8px; - gap: 4px; - padding: 1px 1px 4px; - } - - [data-slot="question-option"] { - display: flex; - align-items: flex-start; - gap: 8px; - padding: 6px 8px; - background-color: var(--surface-raised-stronger-non-alpha); - border: 1px solid var(--border-weak-base); - border-radius: var(--radius-lg); - box-shadow: none; - text-align: left; - width: 100%; - cursor: pointer; - transition: - background-color 0.15s ease, - border-color 0.15s ease, - box-shadow 0.15s ease; - - &:hover:not([data-picked="true"]) { - background-color: var(--background-base); - } - - &[data-picked="true"] { - background-color: var(--surface-interactive-weak); - border-color: transparent; - box-shadow: var(--shadow-xs-border-hover); - } - - &:disabled { - cursor: not-allowed; - opacity: 0.6; - } - } - - [data-slot="question-option-check"] { - display: inline-flex; - transform: translateY(2px); - } - - [data-slot="question-option-box"] { - width: 16px; - height: 16px; - padding: 2px; - border-radius: var(--radius-sm); - border: 1px solid var(--border-weak-base); - display: inline-flex; - align-items: center; - justify-content: center; - flex-shrink: 0; - background-color: transparent; - transition: - background-color 0.15s ease, - border-color 0.15s ease; - - [data-component="icon"] { - opacity: 0; - color: var(--icon-base); - } - - &[data-type="radio"] { - border-radius: 999px; - } - - [data-slot="question-option-radio-dot"] { - width: 6px; - height: 6px; - border-radius: 999px; - background-color: var(--background-stronger); - opacity: 0; - } - - &[data-picked="true"] { - border-color: var(--icon-interactive-base); - - [data-component="icon"] { - opacity: 1; - color: var(--icon-invert-base); - } - - &[data-type="checkbox"] { - background-color: var(--icon-interactive-base); - } - - &[data-type="radio"] { - background-color: var(--icon-interactive-base); - [data-slot="question-option-radio-dot"] { - opacity: 1; - } - } - } - } - - [data-slot="question-option-main"] { - display: flex; - flex-direction: column; - gap: 2px; - min-width: 0; - flex: 1; - } - - [data-slot="option-label"] { - font-family: var(--font-family-sans); - font-size: var(--font-size-base); - font-weight: var(--font-weight-medium); - line-height: var(--line-height-large); - color: var(--text-strong); - } - - [data-slot="option-description"] { - font-family: var(--font-family-sans); - font-size: var(--font-size-small); - font-weight: var(--font-weight-regular); - line-height: var(--line-height-large); - color: var(--text-base); - min-width: 0; - white-space: normal; - overflow: visible; - text-overflow: clip; - } - - [data-slot="question-option"][data-custom="true"] { - [data-slot="option-description"] { - overflow: visible; - text-overflow: clip; - white-space: normal; - overflow-wrap: anywhere; - } - } - [data-slot="question-review"] { display: flex; flex-direction: column; gap: 8px; - padding: 4px 2px; + padding: 4px 10px; } [data-slot="review-title"] { diff --git a/packages/ui/src/components/message-part.tsx b/packages/ui/src/components/message-part.tsx index 84dbc83c1a8..a56362d1825 100644 --- a/packages/ui/src/components/message-part.tsx +++ b/packages/ui/src/components/message-part.tsx @@ -964,22 +964,29 @@ PART_MAPPING["tool"] = function ToolPartDisplay(props) { const render = ToolRegistry.render(part.tool) ?? GenericTool + const dismissed = createMemo( + () => + part.tool === "question" && + part.state.status === "error" && + typeof part.state.error === "string" && + part.state.error.includes("dismissed this question"), + ) + return ( + +
+ + {i18n.t("ui.tool.questions")} dismissed + +
+
+
{(error) => { const cleaned = error().replace("Error: ", "") - if (part.tool === "question" && cleaned.includes("dismissed this question")) { - return ( -
- - {i18n.t("ui.tool.questions")} dismissed - -
- ) - } const [title, ...rest] = cleaned.split(": ") return ( @@ -1016,6 +1023,7 @@ PART_MAPPING["tool"] = function ToolPartDisplay(props) {
+
) }