|
1 | 1 | import { createCliRenderer } from "@opentui/core"; |
2 | 2 | import { render, useKeyboard } from "@opentui/solid"; |
3 | | -import { createMemo, createSignal, For, Show } from "solid-js"; |
| 3 | +import { createMemo, createSignal, For } from "solid-js"; |
4 | 4 | import { |
5 | 5 | applySetupState, |
6 | 6 | type SetupContext, |
@@ -343,7 +343,7 @@ function stateSnapshot(state: SetupState): string[] { |
343 | 343 | return lines; |
344 | 344 | } |
345 | 345 |
|
346 | | -function SetupWizard(props: { |
| 346 | +export function SetupWizard(props: { |
347 | 347 | context: SetupContext; |
348 | 348 | initialState: SetupState; |
349 | 349 | complete: (result: SetupResult) => void; |
@@ -539,102 +539,8 @@ function SetupWizard(props: { |
539 | 539 | backgroundColor="#141a20" |
540 | 540 | gap={1} |
541 | 541 | > |
542 | | - <Show when={phase() === "wizard"}> |
543 | | - <Show |
544 | | - when={activeStep().kind === "summary"} |
545 | | - fallback={ |
546 | | - <box flexDirection="column" gap={1}> |
547 | | - <Show when={activeChoiceStep()}> |
548 | | - {(stepAccessor) => ( |
549 | | - <box flexDirection="column" gap={1}> |
550 | | - <text fg="#8fbcd4">{stepAccessor().eyebrow}</text> |
551 | | - <text> |
552 | | - <strong fg="#f6d365">{stepAccessor().title}</strong> |
553 | | - </text> |
554 | | - <text fg="#d7e3ea">{stepAccessor().description}</text> |
555 | | - <text fg="#7d91a2">{stepAccessor().hint}</text> |
556 | | - <tab_select |
557 | | - focused |
558 | | - options={stepAccessor().options} |
559 | | - selectedIndex={activeChoiceIndex()} |
560 | | - showDescription |
561 | | - onChange={(_index: number, option: WizardChoice | null) => { |
562 | | - if (!option) { |
563 | | - return; |
564 | | - } |
565 | | - setState((current) => { |
566 | | - const next = { ...current }; |
567 | | - stepAccessor().commit(next, option.value); |
568 | | - return next; |
569 | | - }); |
570 | | - }} |
571 | | - onSelect={(_index: number, option: WizardChoice | null) => { |
572 | | - if (!option) { |
573 | | - return; |
574 | | - } |
575 | | - commitAndAdvance(() => { |
576 | | - setState((current) => { |
577 | | - const next = { ...current }; |
578 | | - stepAccessor().commit(next, option.value); |
579 | | - return next; |
580 | | - }); |
581 | | - }); |
582 | | - }} |
583 | | - /> |
584 | | - </box> |
585 | | - )} |
586 | | - </Show> |
587 | | - |
588 | | - <Show when={activeInputStep()}> |
589 | | - {(stepAccessor) => ( |
590 | | - <box flexDirection="column" gap={1}> |
591 | | - <text fg="#8fbcd4">{stepAccessor().eyebrow}</text> |
592 | | - <text> |
593 | | - <strong fg="#f6d365">{stepAccessor().title}</strong> |
594 | | - </text> |
595 | | - <text fg="#d7e3ea">{stepAccessor().description}</text> |
596 | | - <text fg="#7d91a2">{stepAccessor().hint}</text> |
597 | | - <Show |
598 | | - when={stepAccessor().key === "sync-timeout" && state().syncTimeoutError} |
599 | | - > |
600 | | - <text fg="#f8b195">{state().syncTimeoutError}</text> |
601 | | - </Show> |
602 | | - <input |
603 | | - focused |
604 | | - value={stepAccessor().value} |
605 | | - placeholder={stepAccessor().placeholder} |
606 | | - onInput={(value: string) => { |
607 | | - setState((current) => { |
608 | | - const next = { ...current }; |
609 | | - stepAccessor().commit(next, value); |
610 | | - return next; |
611 | | - }); |
612 | | - }} |
613 | | - onSubmit={(value: string) => { |
614 | | - const parsed = Number.parseInt(value.trim(), 10); |
615 | | - const shouldAdvance = |
616 | | - stepAccessor().key !== "sync-timeout" || |
617 | | - (Number.isInteger(parsed) && parsed > 0); |
618 | | - |
619 | | - setState((current) => { |
620 | | - const next = { ...current }; |
621 | | - stepAccessor().commit(next, value); |
622 | | - return next; |
623 | | - }); |
624 | | - |
625 | | - if (shouldAdvance) { |
626 | | - setStepIndex((current) => |
627 | | - getNextWizardStepIndex(current, steps().length), |
628 | | - ); |
629 | | - } |
630 | | - }} |
631 | | - /> |
632 | | - </box> |
633 | | - )} |
634 | | - </Show> |
635 | | - </box> |
636 | | - } |
637 | | - > |
| 542 | + {phase() === "wizard" ? ( |
| 543 | + activeStep().kind === "summary" ? ( |
638 | 544 | <box flexDirection="column" gap={1}> |
639 | 545 | <text fg="#8fbcd4">Ready</text> |
640 | 546 | <text> |
@@ -673,46 +579,142 @@ function SetupWizard(props: { |
673 | 579 | }} |
674 | 580 | /> |
675 | 581 | </box> |
676 | | - </Show> |
677 | | - </Show> |
678 | | - |
679 | | - <Show when={phase() === "saving"}> |
| 582 | + ) : ( |
| 583 | + (() => { |
| 584 | + const choiceStep = activeChoiceStep(); |
| 585 | + if (choiceStep) { |
| 586 | + return ( |
| 587 | + <box flexDirection="column" gap={1}> |
| 588 | + <text fg="#8fbcd4">{choiceStep.eyebrow}</text> |
| 589 | + <text> |
| 590 | + <strong fg="#f6d365">{choiceStep.title}</strong> |
| 591 | + </text> |
| 592 | + <text fg="#d7e3ea">{choiceStep.description}</text> |
| 593 | + <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 | + /> |
| 622 | + </box> |
| 623 | + ); |
| 624 | + } |
| 625 | + |
| 626 | + const inputStep = activeInputStep(); |
| 627 | + if (inputStep) { |
| 628 | + return ( |
| 629 | + <box flexDirection="column" gap={1}> |
| 630 | + <text fg="#8fbcd4">{inputStep.eyebrow}</text> |
| 631 | + <text> |
| 632 | + <strong fg="#f6d365">{inputStep.title}</strong> |
| 633 | + </text> |
| 634 | + <text fg="#d7e3ea">{inputStep.description}</text> |
| 635 | + <text fg="#7d91a2">{inputStep.hint}</text> |
| 636 | + {inputStep.key === "sync-timeout" && state().syncTimeoutError ? ( |
| 637 | + <text fg="#f8b195">{state().syncTimeoutError}</text> |
| 638 | + ) : null} |
| 639 | + <input |
| 640 | + focused |
| 641 | + value={inputStep.value} |
| 642 | + placeholder={inputStep.placeholder} |
| 643 | + onInput={(value: string) => { |
| 644 | + setState((current) => { |
| 645 | + const next = { ...current }; |
| 646 | + inputStep.commit(next, value); |
| 647 | + return next; |
| 648 | + }); |
| 649 | + }} |
| 650 | + onSubmit={(value: string) => { |
| 651 | + const parsed = Number.parseInt(value.trim(), 10); |
| 652 | + const shouldAdvance = |
| 653 | + inputStep.key !== "sync-timeout" || |
| 654 | + (Number.isInteger(parsed) && parsed > 0); |
| 655 | + |
| 656 | + setState((current) => { |
| 657 | + const next = { ...current }; |
| 658 | + inputStep.commit(next, value); |
| 659 | + return next; |
| 660 | + }); |
| 661 | + |
| 662 | + if (shouldAdvance) { |
| 663 | + setStepIndex((current) => |
| 664 | + getNextWizardStepIndex(current, steps().length), |
| 665 | + ); |
| 666 | + } |
| 667 | + }} |
| 668 | + /> |
| 669 | + </box> |
| 670 | + ); |
| 671 | + } |
| 672 | + |
| 673 | + return null; |
| 674 | + })() |
| 675 | + ) |
| 676 | + ) : phase() === "saving" ? ( |
680 | 677 | <box flexDirection="column" gap={1}> |
681 | 678 | <text fg="#8fbcd4">Writing</text> |
682 | 679 | <text> |
683 | 680 | <strong fg="#f6d365">Applying configuration</strong> |
684 | 681 | </text> |
685 | 682 | <text fg="#d7e3ea">Running validations and writing files. Stay on this screen.</text> |
686 | 683 | </box> |
687 | | - </Show> |
688 | | - |
689 | | - <Show when={phase() === "done"}> |
| 684 | + ) : phase() === "done" ? ( |
690 | 685 | <box flexDirection="column" gap={1}> |
691 | 686 | <text fg="#8fbcd4">Complete</text> |
692 | 687 | <text> |
693 | 688 | <strong fg="#9fd3c7">Sandcode is configured.</strong> |
694 | 689 | </text> |
695 | 690 | <text fg="#d7e3ea">Press Enter or Esc to leave setup.</text> |
696 | | - <Show when={result()}> |
697 | | - {(saved) => ( |
| 691 | + {(() => { |
| 692 | + const savedResult = result(); |
| 693 | + if (!savedResult) { |
| 694 | + return null; |
| 695 | + } |
| 696 | + |
| 697 | + return ( |
698 | 698 | <box border borderColor="#1d313a" padding={1} flexDirection="column" gap={1}> |
699 | | - <text fg="#d7e3ea">Config: {saved().configPath}</text> |
700 | | - <Show when={saved().envPath}> |
701 | | - {(envPath) => <text fg="#d7e3ea">Env: {envPath()}</text>} |
702 | | - </Show> |
| 699 | + <text fg="#d7e3ea">Config: {savedResult.configPath}</text> |
| 700 | + {savedResult.envPath ? ( |
| 701 | + <text fg="#d7e3ea">Env: {savedResult.envPath}</text> |
| 702 | + ) : null} |
703 | 703 | </box> |
704 | | - )} |
705 | | - </Show> |
| 704 | + ); |
| 705 | + })()} |
706 | 706 | </box> |
707 | | - </Show> |
708 | | - |
709 | | - <Show when={phase() === "error"}> |
| 707 | + ) : phase() === "error" ? ( |
710 | 708 | <box flexDirection="column" gap={1}> |
711 | 709 | <text fg="#f8b195">Validation failed</text> |
712 | 710 | <text fg="#fbe4d8">{errorMessage()}</text> |
713 | 711 | <text fg="#7d91a2">Press Enter or Esc to go back and edit the setup values.</text> |
714 | 712 | </box> |
715 | | - </Show> |
| 713 | + ) : null} |
| 714 | + |
| 715 | + <box marginTop="auto" border borderColor="#1d313a" padding={1}> |
| 716 | + <text fg="#7d91a2">Esc goes back. Ctrl+C exits setup immediately.</text> |
| 717 | + </box> |
716 | 718 | </box> |
717 | 719 | </box> |
718 | 720 | </box> |
|
0 commit comments