11import { createCliRenderer } from "@opentui/core" ;
22import { render , useKeyboard } from "@opentui/solid" ;
3- import { createMemo , createSignal , For } from "solid-js" ;
3+ import { createEffect , createMemo , createSignal , For } from "solid-js" ;
44import {
55 applySetupState ,
66 type SetupContext ,
99 type SetupState ,
1010 summarizeSetupContext ,
1111} from "./setup-core.js" ;
12+ import { isSubmitKey , parseChoiceShortcut } from "./setup-ui-keys.js" ;
1213import { getNextWizardStepIndex } from "./setup-ui-state.js" ;
1314
1415type WizardChoice = {
@@ -357,6 +358,7 @@ export function SetupWizard(props: {
357358 ) ;
358359 const [ errorMessage , setErrorMessage ] = createSignal < string > ( ) ;
359360 const [ result , setResult ] = createSignal < SetupResult > ( ) ;
361+ const [ summaryActionIndex , setSummaryActionIndex ] = createSignal ( 0 ) ;
360362
361363 const steps = createMemo ( ( ) => buildSteps ( { ...state ( ) } ) ) ;
362364 const activeStep = createMemo ( ( ) => steps ( ) [ Math . min ( stepIndex ( ) , steps ( ) . length - 1 ) ] ) ;
@@ -377,6 +379,12 @@ export function SetupWizard(props: {
377379 return index >= 0 ? index : 0 ;
378380 } ) ;
379381
382+ createEffect ( ( ) => {
383+ if ( activeStep ( ) . kind === "summary" ) {
384+ setSummaryActionIndex ( 0 ) ;
385+ }
386+ } ) ;
387+
380388 const goBack = ( ) : void => {
381389 if ( phase ( ) === "saving" ) {
382390 return ;
@@ -436,6 +444,68 @@ export function SetupWizard(props: {
436444 }
437445 } ;
438446
447+ const updateChoiceSelection = ( offset : - 1 | 1 ) : void => {
448+ const step = activeChoiceStep ( ) ;
449+ if ( ! step ) {
450+ return ;
451+ }
452+
453+ const nextIndex = Math . max ( 0 , Math . min ( activeChoiceIndex ( ) + offset , step . options . length - 1 ) ) ;
454+ const option = step . options [ nextIndex ] ;
455+ if ( ! option ) {
456+ return ;
457+ }
458+
459+ setState ( ( current ) => {
460+ const next = { ...current } ;
461+ step . commit ( next , option . value ) ;
462+ return next ;
463+ } ) ;
464+ } ;
465+
466+ const commitChoiceAndAdvance = ( ) : void => {
467+ const step = activeChoiceStep ( ) ;
468+ if ( ! step ) {
469+ return ;
470+ }
471+
472+ const option = step . options [ activeChoiceIndex ( ) ] ;
473+ if ( ! option ) {
474+ return ;
475+ }
476+
477+ commitAndAdvance ( ( ) => {
478+ setState ( ( current ) => {
479+ const next = { ...current } ;
480+ step . commit ( next , option . value ) ;
481+ return next ;
482+ } ) ;
483+ } ) ;
484+ } ;
485+
486+ const triggerSummaryAction = ( ) : void => {
487+ if ( summaryActionIndex ( ) === 1 ) {
488+ props . cancel ( new Error ( "Setup cancelled." ) ) ;
489+ return ;
490+ }
491+
492+ void save ( ) ;
493+ } ;
494+
495+ const triggerSummaryShortcut = ( index : number ) : void => {
496+ if ( index < 0 || index > 1 ) {
497+ return ;
498+ }
499+
500+ setSummaryActionIndex ( index ) ;
501+ if ( index === 1 ) {
502+ props . cancel ( new Error ( "Setup cancelled." ) ) ;
503+ return ;
504+ }
505+
506+ void save ( ) ;
507+ } ;
508+
439509 useKeyboard (
440510 ( event : {
441511 ctrl : boolean ;
@@ -457,7 +527,81 @@ export function SetupWizard(props: {
457527 return ;
458528 }
459529
460- if ( ( phase ( ) === "done" || phase ( ) === "error" ) && event . name === "return" ) {
530+ if ( phase ( ) === "wizard" && activeStep ( ) . kind === "choice" ) {
531+ const shortcutIndex = parseChoiceShortcut ( event . name ) ;
532+ if ( shortcutIndex !== undefined ) {
533+ const step = activeChoiceStep ( ) ;
534+ const option = step ?. options [ shortcutIndex ] ;
535+ if ( ! option || ! step ) {
536+ return ;
537+ }
538+
539+ event . preventDefault ( ) ;
540+ event . stopPropagation ( ) ;
541+ commitAndAdvance ( ( ) => {
542+ setState ( ( current ) => {
543+ const next = { ...current } ;
544+ step . commit ( next , option . value ) ;
545+ return next ;
546+ } ) ;
547+ } ) ;
548+ return ;
549+ }
550+
551+ if ( event . name === "left" ) {
552+ event . preventDefault ( ) ;
553+ event . stopPropagation ( ) ;
554+ updateChoiceSelection ( - 1 ) ;
555+ return ;
556+ }
557+
558+ if ( event . name === "right" ) {
559+ event . preventDefault ( ) ;
560+ event . stopPropagation ( ) ;
561+ updateChoiceSelection ( 1 ) ;
562+ return ;
563+ }
564+
565+ if ( isSubmitKey ( event . name ) ) {
566+ event . preventDefault ( ) ;
567+ event . stopPropagation ( ) ;
568+ commitChoiceAndAdvance ( ) ;
569+ return ;
570+ }
571+ }
572+
573+ if ( phase ( ) === "wizard" && activeStep ( ) . kind === "summary" ) {
574+ const shortcutIndex = parseChoiceShortcut ( event . name ) ;
575+ if ( shortcutIndex !== undefined ) {
576+ event . preventDefault ( ) ;
577+ event . stopPropagation ( ) ;
578+ triggerSummaryShortcut ( shortcutIndex ) ;
579+ return ;
580+ }
581+
582+ if ( event . name === "left" ) {
583+ event . preventDefault ( ) ;
584+ event . stopPropagation ( ) ;
585+ setSummaryActionIndex ( 0 ) ;
586+ return ;
587+ }
588+
589+ if ( event . name === "right" ) {
590+ event . preventDefault ( ) ;
591+ event . stopPropagation ( ) ;
592+ setSummaryActionIndex ( 1 ) ;
593+ return ;
594+ }
595+
596+ if ( isSubmitKey ( event . name ) ) {
597+ event . preventDefault ( ) ;
598+ event . stopPropagation ( ) ;
599+ triggerSummaryAction ( ) ;
600+ return ;
601+ }
602+ }
603+
604+ if ( ( phase ( ) === "done" || phase ( ) === "error" ) && isSubmitKey ( event . name ) ) {
461605 event . preventDefault ( ) ;
462606 event . stopPropagation ( ) ;
463607 goBack ( ) ;
@@ -554,30 +698,26 @@ export function SetupWizard(props: {
554698 { ( line ) => < text fg = "#d7e3ea" > { line } </ text > }
555699 </ For >
556700 </ box >
557- < tab_select
558- focused
559- options = { [
560- {
561- name : "Save" ,
562- description : "Write config and finish setup." ,
563- value : "save" ,
564- } ,
565- {
566- name : "Cancel" ,
567- description : "Exit without writing files." ,
568- value : "cancel" ,
569- } ,
570- ] }
571- selectedIndex = { 0 }
572- showDescription
573- onSelect = { ( _index : number , option : WizardChoice | null ) => {
574- if ( option ?. value === "cancel" ) {
575- props . cancel ( new Error ( "Setup cancelled." ) ) ;
576- return ;
577- }
578- void save ( ) ;
579- } }
580- />
701+ < box flexDirection = "column" gap = { 1 } >
702+ < box >
703+ < For each = { [ "Save" , "Cancel" ] } >
704+ { ( label , index ) => (
705+ < text
706+ backgroundColor = { index ( ) === summaryActionIndex ( ) ? "#334455" : "#1a1a1a" }
707+ fg = { index ( ) === summaryActionIndex ( ) ? "#ffff00" : "#ffffff" }
708+ >
709+ { " " }
710+ { label } { " " }
711+ </ text >
712+ ) }
713+ </ For >
714+ </ box >
715+ < text fg = "#cccccc" >
716+ { summaryActionIndex ( ) === 0
717+ ? "Write config and finish setup."
718+ : "Exit without writing files." }
719+ </ text >
720+ </ box >
581721 </ box >
582722 ) : (
583723 ( ( ) => {
@@ -591,34 +731,26 @@ export function SetupWizard(props: {
591731 </ text >
592732 < text fg = "#d7e3ea" > { choiceStep . description } </ text >
593733 < text fg = "#7d91a2" > { choiceStep . hint } </ text >
594- < tab_select
595- focused
596- options = { choiceStep . options }
597- selectedIndex = { activeChoiceIndex ( ) }
598- showDescription
599- onChange = { ( _index : number , option : WizardChoice | null ) => {
600- if ( ! option ) {
601- return ;
602- }
603- setState ( ( current ) => {
604- const next = { ...current } ;
605- choiceStep . commit ( next , option . value ) ;
606- return next ;
607- } ) ;
608- } }
609- onSelect = { ( _index : number , option : WizardChoice | null ) => {
610- if ( ! option ) {
611- return ;
612- }
613- commitAndAdvance ( ( ) => {
614- setState ( ( current ) => {
615- const next = { ...current } ;
616- choiceStep . commit ( next , option . value ) ;
617- return next ;
618- } ) ;
619- } ) ;
620- } }
621- />
734+ < box flexDirection = "column" gap = { 1 } >
735+ < box >
736+ < For each = { choiceStep . options } >
737+ { ( option , index ) => (
738+ < text
739+ backgroundColor = {
740+ index ( ) === activeChoiceIndex ( ) ? "#334455" : "#1a1a1a"
741+ }
742+ fg = { index ( ) === activeChoiceIndex ( ) ? "#ffff00" : "#ffffff" }
743+ >
744+ { " " }
745+ { option . name } { " " }
746+ </ text >
747+ ) }
748+ </ For >
749+ </ box >
750+ < text fg = "#cccccc" >
751+ { choiceStep . options [ activeChoiceIndex ( ) ] ?. description ?? "" }
752+ </ text >
753+ </ box >
622754 </ box >
623755 ) ;
624756 }
@@ -713,7 +845,9 @@ export function SetupWizard(props: {
713845 ) : null }
714846
715847 < box marginTop = "auto" border borderColor = "#1d313a" padding = { 1 } >
716- < text fg = "#7d91a2" > Esc goes back. Ctrl+C exits setup immediately.</ text >
848+ < text fg = "#7d91a2" >
849+ Esc goes back. Ctrl+C exits setup immediately. 1-9 selects a visible choice.
850+ </ text >
717851 </ box >
718852 </ box >
719853 </ box >
0 commit comments