11import { For , Show , createMemo , onCleanup , onMount , type Component } from "solid-js"
22import { createStore } from "solid-js/store"
33import { Button } from "@opencode-ai/ui/button"
4+ import { DockPrompt } from "@opencode-ai/ui/dock-prompt"
45import { Icon } from "@opencode-ai/ui/icon"
56import { showToast } from "@opencode-ai/ui/toast"
67import type { QuestionAnswer , QuestionRequest } from "@opencode-ai/sdk/v2"
@@ -232,9 +233,11 @@ export const QuestionDock: Component<{ request: QuestionRequest }> = (props) =>
232233 }
233234
234235 return (
235- < div data-component = "question-prompt" ref = { ( el ) => ( root = el ) } >
236- < div data-slot = "question-body" >
237- < div data-slot = "question-header" >
236+ < DockPrompt
237+ kind = "question"
238+ ref = { ( el ) => ( root = el ) }
239+ header = {
240+ < >
238241 < div data-slot = "question-header-title" > { summary ( ) } </ div >
239242 < div data-slot = "question-progress" >
240243 < For each = { questions ( ) } >
@@ -254,172 +257,169 @@ export const QuestionDock: Component<{ request: QuestionRequest }> = (props) =>
254257 ) }
255258 </ For >
256259 </ div >
257- </ div >
258-
259- < div data-slot = "question-content" >
260- < div data-slot = "question-text" > { question ( ) ?. question } </ div >
261- < Show when = { multi ( ) } fallback = { < div data-slot = "question-hint" > { language . t ( "ui.question.singleHint" ) } </ div > } >
262- < div data-slot = "question-hint" > { language . t ( "ui.question.multiHint" ) } </ div >
263- </ Show >
264- < div data-slot = "question-options" >
265- < For each = { options ( ) } >
266- { ( opt , i ) => {
267- const picked = ( ) => store . answers [ store . tab ] ?. includes ( opt . label ) ?? false
268- return (
269- < button
270- data-slot = "question-option"
271- data-picked = { picked ( ) }
272- role = { multi ( ) ? "checkbox" : "radio" }
273- aria-checked = { picked ( ) }
274- disabled = { store . sending }
275- onClick = { ( ) => selectOption ( i ( ) ) }
276- >
277- < span data-slot = "question-option-check" aria-hidden = "true" >
278- < span
279- data-slot = "question-option-box"
280- data-type = { multi ( ) ? "checkbox" : "radio" }
281- data-picked = { picked ( ) }
282- >
283- < Show when = { multi ( ) } fallback = { < span data-slot = "question-option-radio-dot" /> } >
284- < Icon name = "check-small" size = "small" />
285- </ Show >
286- </ span >
287- </ span >
288- < span data-slot = "question-option-main" >
289- < span data-slot = "option-label" > { opt . label } </ span >
290- < Show when = { opt . description } >
291- < span data-slot = "option-description" > { opt . description } </ span >
292- </ Show >
293- </ span >
294- </ button >
295- )
296- } }
297- </ For >
298-
299- < Show
300- when = { store . editing }
301- fallback = {
302- < button
303- data-slot = "question-option"
304- data-custom = "true"
305- data-picked = { on ( ) }
306- role = { multi ( ) ? "checkbox" : "radio" }
307- aria-checked = { on ( ) }
308- disabled = { store . sending }
309- onClick = { customOpen }
310- >
260+ </ >
261+ }
262+ footer = {
263+ < >
264+ < Button variant = "ghost" size = "large" disabled = { store . sending } onClick = { reject } >
265+ { language . t ( "ui.common.dismiss" ) }
266+ </ Button >
267+ < div data-slot = "question-footer-actions" >
268+ < Show when = { store . tab > 0 } >
269+ < Button variant = "secondary" size = "large" disabled = { store . sending } onClick = { back } >
270+ { language . t ( "ui.common.back" ) }
271+ </ Button >
272+ </ Show >
273+ < Button variant = { last ( ) ? "primary" : "secondary" } size = "large" disabled = { store . sending } onClick = { next } >
274+ { last ( ) ? language . t ( "ui.common.submit" ) : language . t ( "ui.common.next" ) }
275+ </ Button >
276+ </ div >
277+ </ >
278+ }
279+ >
280+ < div data-slot = "question-text" > { question ( ) ?. question } </ div >
281+ < Show when = { multi ( ) } fallback = { < div data-slot = "question-hint" > { language . t ( "ui.question.singleHint" ) } </ div > } >
282+ < div data-slot = "question-hint" > { language . t ( "ui.question.multiHint" ) } </ div >
283+ </ Show >
284+ < div data-slot = "question-options" >
285+ < For each = { options ( ) } >
286+ { ( opt , i ) => {
287+ const picked = ( ) => store . answers [ store . tab ] ?. includes ( opt . label ) ?? false
288+ return (
289+ < button
290+ data-slot = "question-option"
291+ data-picked = { picked ( ) }
292+ role = { multi ( ) ? "checkbox" : "radio" }
293+ aria-checked = { picked ( ) }
294+ disabled = { store . sending }
295+ onClick = { ( ) => selectOption ( i ( ) ) }
296+ >
297+ < span data-slot = "question-option-check" aria-hidden = "true" >
311298 < span
312- data-slot = "question-option-check"
313- aria-hidden = "true"
314- onClick = { ( e ) => {
315- e . preventDefault ( )
316- e . stopPropagation ( )
317- customToggle ( )
318- } }
299+ data-slot = "question-option-box"
300+ data-type = { multi ( ) ? "checkbox" : "radio" }
301+ data-picked = { picked ( ) }
319302 >
320- < span data-slot = "question-option-box" data-type = { multi ( ) ? "checkbox" : "radio" } data-picked = { on ( ) } >
321- < Show when = { multi ( ) } fallback = { < span data-slot = "question-option-radio-dot" /> } >
322- < Icon name = "check-small" size = "small" />
323- </ Show >
324- </ span >
325- </ span >
326- < span data-slot = "question-option-main" >
327- < span data-slot = "option-label" > { language . t ( "ui.messagePart.option.typeOwnAnswer" ) } </ span >
328- < span data-slot = "option-description" >
329- { input ( ) || language . t ( "ui.question.custom.placeholder" ) }
330- </ span >
303+ < Show when = { multi ( ) } fallback = { < span data-slot = "question-option-radio-dot" /> } >
304+ < Icon name = "check-small" size = "small" />
305+ </ Show >
331306 </ span >
332- </ button >
307+ </ span >
308+ < span data-slot = "question-option-main" >
309+ < span data-slot = "option-label" > { opt . label } </ span >
310+ < Show when = { opt . description } >
311+ < span data-slot = "option-description" > { opt . description } </ span >
312+ </ Show >
313+ </ span >
314+ </ button >
315+ )
316+ } }
317+ </ For >
318+
319+ < Show
320+ when = { store . editing }
321+ fallback = {
322+ < button
323+ data-slot = "question-option"
324+ data-custom = "true"
325+ data-picked = { on ( ) }
326+ role = { multi ( ) ? "checkbox" : "radio" }
327+ aria-checked = { on ( ) }
328+ disabled = { store . sending }
329+ onClick = { customOpen }
330+ >
331+ < span
332+ data-slot = "question-option-check"
333+ aria-hidden = "true"
334+ onClick = { ( e ) => {
335+ e . preventDefault ( )
336+ e . stopPropagation ( )
337+ customToggle ( )
338+ } }
339+ >
340+ < span data-slot = "question-option-box" data-type = { multi ( ) ? "checkbox" : "radio" } data-picked = { on ( ) } >
341+ < Show when = { multi ( ) } fallback = { < span data-slot = "question-option-radio-dot" /> } >
342+ < Icon name = "check-small" size = "small" />
343+ </ Show >
344+ </ span >
345+ </ span >
346+ < span data-slot = "question-option-main" >
347+ < span data-slot = "option-label" > { language . t ( "ui.messagePart.option.typeOwnAnswer" ) } </ span >
348+ < span data-slot = "option-description" > { input ( ) || language . t ( "ui.question.custom.placeholder" ) } </ span >
349+ </ span >
350+ </ button >
351+ }
352+ >
353+ < form
354+ data-slot = "question-option"
355+ data-custom = "true"
356+ data-picked = { on ( ) }
357+ role = { multi ( ) ? "checkbox" : "radio" }
358+ aria-checked = { on ( ) }
359+ onMouseDown = { ( e ) => {
360+ if ( store . sending ) {
361+ e . preventDefault ( )
362+ return
333363 }
364+ if ( e . target instanceof HTMLTextAreaElement ) return
365+ const input = e . currentTarget . querySelector ( '[data-slot="question-custom-input"]' )
366+ if ( input instanceof HTMLTextAreaElement ) input . focus ( )
367+ } }
368+ onSubmit = { ( e ) => {
369+ e . preventDefault ( )
370+ commitCustom ( )
371+ } }
372+ >
373+ < span
374+ data-slot = "question-option-check"
375+ aria-hidden = "true"
376+ onClick = { ( e ) => {
377+ e . preventDefault ( )
378+ e . stopPropagation ( )
379+ customToggle ( )
380+ } }
334381 >
335- < form
336- data-slot = "question-option"
337- data-custom = "true"
338- data-picked = { on ( ) }
339- role = { multi ( ) ? "checkbox" : "radio" }
340- aria-checked = { on ( ) }
341- onMouseDown = { ( e ) => {
342- if ( store . sending ) {
382+ < span data-slot = "question-option-box" data-type = { multi ( ) ? "checkbox" : "radio" } data-picked = { on ( ) } >
383+ < Show when = { multi ( ) } fallback = { < span data-slot = "question-option-radio-dot" /> } >
384+ < Icon name = "check-small" size = "small" />
385+ </ Show >
386+ </ span >
387+ </ span >
388+ < span data-slot = "question-option-main" >
389+ < span data-slot = "option-label" > { language . t ( "ui.messagePart.option.typeOwnAnswer" ) } </ span >
390+ < textarea
391+ ref = { ( el ) =>
392+ setTimeout ( ( ) => {
393+ el . focus ( )
394+ el . style . height = "0px"
395+ el . style . height = `${ el . scrollHeight } px`
396+ } , 0 )
397+ }
398+ data-slot = "question-custom-input"
399+ placeholder = { language . t ( "ui.question.custom.placeholder" ) }
400+ value = { input ( ) }
401+ rows = { 1 }
402+ disabled = { store . sending }
403+ onKeyDown = { ( e ) => {
404+ if ( e . key === "Escape" ) {
343405 e . preventDefault ( )
406+ setStore ( "editing" , false )
344407 return
345408 }
346- if ( e . target instanceof HTMLTextAreaElement ) return
347- const input = e . currentTarget . querySelector ( '[data-slot="question-custom-input"]' )
348- if ( input instanceof HTMLTextAreaElement ) input . focus ( )
349- } }
350- onSubmit = { ( e ) => {
409+ if ( e . key !== "Enter" || e . shiftKey ) return
351410 e . preventDefault ( )
352411 commitCustom ( )
353412 } }
354- >
355- < span
356- data-slot = "question-option-check"
357- aria-hidden = "true"
358- onClick = { ( e ) => {
359- e . preventDefault ( )
360- e . stopPropagation ( )
361- customToggle ( )
362- } }
363- >
364- < span data-slot = "question-option-box" data-type = { multi ( ) ? "checkbox" : "radio" } data-picked = { on ( ) } >
365- < Show when = { multi ( ) } fallback = { < span data-slot = "question-option-radio-dot" /> } >
366- < Icon name = "check-small" size = "small" />
367- </ Show >
368- </ span >
369- </ span >
370- < span data-slot = "question-option-main" >
371- < span data-slot = "option-label" > { language . t ( "ui.messagePart.option.typeOwnAnswer" ) } </ span >
372- < textarea
373- ref = { ( el ) =>
374- setTimeout ( ( ) => {
375- el . focus ( )
376- el . style . height = "0px"
377- el . style . height = `${ el . scrollHeight } px`
378- } , 0 )
379- }
380- data-slot = "question-custom-input"
381- placeholder = { language . t ( "ui.question.custom.placeholder" ) }
382- value = { input ( ) }
383- rows = { 1 }
384- disabled = { store . sending }
385- onKeyDown = { ( e ) => {
386- if ( e . key === "Escape" ) {
387- e . preventDefault ( )
388- setStore ( "editing" , false )
389- return
390- }
391- if ( e . key !== "Enter" || e . shiftKey ) return
392- e . preventDefault ( )
393- commitCustom ( )
394- } }
395- onInput = { ( e ) => {
396- customUpdate ( e . currentTarget . value )
397- e . currentTarget . style . height = "0px"
398- e . currentTarget . style . height = `${ e . currentTarget . scrollHeight } px`
399- } }
400- />
401- </ span >
402- </ form >
403- </ Show >
404- </ div >
405- </ div >
406- </ div >
407-
408- < div data-slot = "question-footer" >
409- < Button variant = "ghost" size = "large" disabled = { store . sending } onClick = { reject } >
410- { language . t ( "ui.common.dismiss" ) }
411- </ Button >
412- < div data-slot = "question-footer-actions" >
413- < Show when = { store . tab > 0 } >
414- < Button variant = "secondary" size = "large" disabled = { store . sending } onClick = { back } >
415- { language . t ( "ui.common.back" ) }
416- </ Button >
417- </ Show >
418- < Button variant = { last ( ) ? "primary" : "secondary" } size = "large" disabled = { store . sending } onClick = { next } >
419- { last ( ) ? language . t ( "ui.common.submit" ) : language . t ( "ui.common.next" ) }
420- </ Button >
421- </ div >
413+ onInput = { ( e ) => {
414+ customUpdate ( e . currentTarget . value )
415+ e . currentTarget . style . height = "0px"
416+ e . currentTarget . style . height = `${ e . currentTarget . scrollHeight } px`
417+ } }
418+ />
419+ </ span >
420+ </ form >
421+ </ Show >
422422 </ div >
423- </ div >
423+ </ DockPrompt >
424424 )
425425}
0 commit comments