Skip to content

Commit dcfeb8e

Browse files
alltomatosclaude
andcommitted
feat(app): migrate dialog-select-server.tsx to v2 components
Replaces legacy Button/IconButton/Icon/TextField/DropdownMenu with ButtonV2/IconButtonV2/IconV2/Field+TextInputV2/MenuV2 across the server list, add/edit form, and remove-confirmation dialog. Implements issue #6 from the UI/UX roadmap. Co-Authored-By: Claude Sonnet 5 <[email protected]>
1 parent 703b2df commit dcfeb8e

1 file changed

Lines changed: 111 additions & 87 deletions

File tree

‎packages/app/src/components/dialog-select-server.tsx‎

Lines changed: 111 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
1-
import { Button } from "@opencode-ai/ui/button"
21
import { useDialog } from "@opencode-ai/ui/context/dialog"
32
import { Dialog } from "@opencode-ai/ui/dialog"
4-
import { DropdownMenu } from "@opencode-ai/ui/dropdown-menu"
53
import { Icon } from "@opencode-ai/ui/icon"
6-
import { IconButton } from "@opencode-ai/ui/icon-button"
4+
import { Icon as IconV2 } from "@opencode-ai/ui/v2/icon"
75
import { List } from "@opencode-ai/ui/list"
8-
import { TextField } from "@opencode-ai/ui/text-field"
6+
import { ButtonV2 } from "@opencode-ai/ui/v2/button-v2"
7+
import { IconButtonV2 } from "@opencode-ai/ui/v2/icon-button-v2"
8+
import { MenuV2 } from "@opencode-ai/ui/v2/menu-v2"
9+
import { Field } from "@opencode-ai/ui/v2/field-v2"
10+
import { TextInputV2 } from "@opencode-ai/ui/v2/text-input-v2"
911
import { useMutation } from "@tanstack/solid-query"
1012
import { showToast } from "@/utils/toast"
1113
import { useNavigate } from "@solidjs/router"
@@ -127,48 +129,67 @@ function ServerForm(props: ServerFormProps) {
127129
return (
128130
<div>
129131
<div class="bg-surface-base rounded-md p-5 flex flex-col gap-3">
130-
<div class="flex-1 min-w-0 [&_[data-slot=input-wrapper]]:relative">
131-
<TextField
132-
type="text"
133-
label={language.t("dialog.server.add.url")}
134-
placeholder={props.placeholder}
135-
value={props.value}
136-
autofocus
137-
validationState={props.error ? "invalid" : "valid"}
138-
error={props.error}
139-
disabled={props.busy}
140-
onChange={props.onChange}
141-
onKeyDown={keyDown}
142-
/>
143-
</div>
144-
<TextField
145-
type="text"
146-
label={language.t("dialog.server.add.name")}
147-
placeholder={language.t("dialog.server.add.namePlaceholder")}
148-
value={props.name}
149-
disabled={props.busy}
150-
onChange={props.onNameChange}
151-
onKeyDown={keyDown}
152-
/>
132+
<Field invalid={!!props.error}>
133+
<Field.Label>{language.t("dialog.server.add.url")}</Field.Label>
134+
<Field.Control>
135+
<TextInputV2
136+
class="!w-full"
137+
type="text"
138+
placeholder={props.placeholder}
139+
value={props.value}
140+
autofocus
141+
disabled={props.busy}
142+
onInput={(e) => props.onChange(e.currentTarget.value)}
143+
onKeyDown={keyDown}
144+
/>
145+
</Field.Control>
146+
<Show when={props.error}>
147+
<Field.Suffix class="text-v2-state-fg-danger">{props.error}</Field.Suffix>
148+
</Show>
149+
</Field>
150+
<Field>
151+
<Field.Label>{language.t("dialog.server.add.name")}</Field.Label>
152+
<Field.Control>
153+
<TextInputV2
154+
class="!w-full"
155+
type="text"
156+
placeholder={language.t("dialog.server.add.namePlaceholder")}
157+
value={props.name}
158+
disabled={props.busy}
159+
onInput={(e) => props.onNameChange(e.currentTarget.value)}
160+
onKeyDown={keyDown}
161+
/>
162+
</Field.Control>
163+
</Field>
153164
<div class="grid grid-cols-2 gap-2 min-w-0">
154-
<TextField
155-
type="text"
156-
label={language.t("dialog.server.add.username")}
157-
placeholder={language.t("dialog.server.add.usernamePlaceholder")}
158-
value={props.username}
159-
disabled={props.busy}
160-
onChange={props.onUsernameChange}
161-
onKeyDown={keyDown}
162-
/>
163-
<TextField
164-
type="password"
165-
label={language.t("dialog.server.add.password")}
166-
placeholder={language.t("dialog.server.add.passwordPlaceholder")}
167-
value={props.password}
168-
disabled={props.busy}
169-
onChange={props.onPasswordChange}
170-
onKeyDown={keyDown}
171-
/>
165+
<Field>
166+
<Field.Label>{language.t("dialog.server.add.username")}</Field.Label>
167+
<Field.Control>
168+
<TextInputV2
169+
class="!w-full"
170+
type="text"
171+
placeholder={language.t("dialog.server.add.usernamePlaceholder")}
172+
value={props.username}
173+
disabled={props.busy}
174+
onInput={(e) => props.onUsernameChange(e.currentTarget.value)}
175+
onKeyDown={keyDown}
176+
/>
177+
</Field.Control>
178+
</Field>
179+
<Field>
180+
<Field.Label>{language.t("dialog.server.add.password")}</Field.Label>
181+
<Field.Control>
182+
<TextInputV2
183+
class="!w-full"
184+
type="password"
185+
placeholder={language.t("dialog.server.add.passwordPlaceholder")}
186+
value={props.password}
187+
disabled={props.busy}
188+
onInput={(e) => props.onPasswordChange(e.currentTarget.value)}
189+
onKeyDown={keyDown}
190+
/>
191+
</Field.Control>
192+
</Field>
172193
</div>
173194
</div>
174195
</div>
@@ -516,7 +537,12 @@ export function useServerManagementController(options: { onSelect?: () => void;
516537
if (!isFormMode()) return language.t("dialog.server.title")
517538
return (
518539
<div class="flex items-center gap-2 -ml-2">
519-
<IconButton icon="arrow-left" variant="ghost" onClick={resetForm} aria-label={language.t("common.goBack")} />
540+
<IconButtonV2
541+
variant="ghost"
542+
onClick={resetForm}
543+
aria-label={language.t("common.goBack")}
544+
icon={<Icon name="arrow-left" size="small" />}
545+
/>
520546
<span>{isAddMode() ? language.t("dialog.server.add.title") : language.t("dialog.server.edit.title")}</span>
521547
</div>
522548
)
@@ -550,19 +576,19 @@ export function useServerManagementController(options: { onSelect?: () => void;
550576
<span class="text-14-regular text-text-strong">{language.t("server.remove.confirm", { name })}</span>
551577
</div>
552578
<div class="flex justify-end gap-2">
553-
<Button variant="ghost" size="large" onClick={() => dialog.close()}>
579+
<ButtonV2 variant="ghost-muted" size="large" onClick={() => dialog.close()}>
554580
{language.t("common.cancel")}
555-
</Button>
556-
<Button
557-
variant="primary"
581+
</ButtonV2>
582+
<ButtonV2
583+
variant="danger"
558584
size="large"
559585
onClick={() => {
560586
dialog.close()
561587
void removeNow(key)
562588
}}
563589
>
564590
{language.t("server.remove.button")}
565-
</Button>
591+
</ButtonV2>
566592
</div>
567593
</div>
568594
</Dialog>
@@ -643,55 +669,53 @@ export function ServerConnectionList(props: { controller: ReturnType<typeof useS
643669
/>
644670
<div class="flex items-center justify-center gap-4 pl-4">
645671
<Show when={props.controller.current() && ServerConnection.key(props.controller.current()!) === key}>
646-
<Icon name="check" class="h-6" />
672+
<IconV2 name="check" class="h-6" />
647673
</Show>
648674

649675
<Show when={i.type === "http"}>
650-
<DropdownMenu>
651-
<DropdownMenu.Trigger
652-
as={IconButton}
653-
icon="dot-grid"
676+
<MenuV2>
677+
<MenuV2.Trigger
678+
as={IconButtonV2}
654679
variant="ghost"
655680
class="shrink-0 size-8 hover:bg-surface-base-hover data-[expanded]:bg-surface-base-active"
656681
onClick={(e: MouseEvent) => e.stopPropagation()}
657682
onPointerDown={(e: PointerEvent) => e.stopPropagation()}
683+
icon={<IconV2 name="outline-dots" size="small" />}
658684
/>
659-
<DropdownMenu.Portal>
660-
<DropdownMenu.Content class="mt-1">
661-
<DropdownMenu.Item
685+
<MenuV2.Portal>
686+
<MenuV2.Content class="mt-1">
687+
<MenuV2.Item
662688
onSelect={() => {
663689
if (i.type !== "http") return
664690
props.controller.startEdit(i)
665691
}}
666692
>
667-
<Icon name="edit" size="small" />
668-
<DropdownMenu.ItemLabel>{language.t("dialog.server.menu.edit")}</DropdownMenu.ItemLabel>
669-
</DropdownMenu.Item>
693+
<IconV2 name="edit" size="small" />
694+
{language.t("dialog.server.menu.edit")}
695+
</MenuV2.Item>
670696
<Show when={props.controller.canDefault() && props.controller.defaultKey() !== key}>
671-
<DropdownMenu.Item onSelect={() => props.controller.setDefault(key)}>
672-
<Icon name="check" size="small" />
673-
<DropdownMenu.ItemLabel>{language.t("dialog.server.menu.default")}</DropdownMenu.ItemLabel>
674-
</DropdownMenu.Item>
697+
<MenuV2.Item onSelect={() => props.controller.setDefault(key)}>
698+
<IconV2 name="check" size="small" />
699+
{language.t("dialog.server.menu.default")}
700+
</MenuV2.Item>
675701
</Show>
676702
<Show when={props.controller.canDefault() && props.controller.defaultKey() === key}>
677-
<DropdownMenu.Item onSelect={() => props.controller.setDefault(null)}>
678-
<Icon name="close" size="small" />
679-
<DropdownMenu.ItemLabel>
680-
{language.t("dialog.server.menu.defaultRemove")}
681-
</DropdownMenu.ItemLabel>
682-
</DropdownMenu.Item>
703+
<MenuV2.Item onSelect={() => props.controller.setDefault(null)}>
704+
<IconV2 name="close" size="small" />
705+
{language.t("dialog.server.menu.defaultRemove")}
706+
</MenuV2.Item>
683707
</Show>
684-
<DropdownMenu.Separator />
685-
<DropdownMenu.Item
708+
<MenuV2.Separator />
709+
<MenuV2.Item
686710
onSelect={() => props.controller.handleRemove(ServerConnection.key(i), i)}
687-
class="text-text-on-critical-base hover:bg-surface-critical-weak"
711+
class="text-v2-state-fg-danger"
688712
>
689713
<Icon name="trash" size="small" />
690-
<DropdownMenu.ItemLabel>{language.t("dialog.server.menu.delete")}</DropdownMenu.ItemLabel>
691-
</DropdownMenu.Item>
692-
</DropdownMenu.Content>
693-
</DropdownMenu.Portal>
694-
</DropdownMenu>
714+
{language.t("dialog.server.menu.delete")}
715+
</MenuV2.Item>
716+
</MenuV2.Content>
717+
</MenuV2.Portal>
718+
</MenuV2>
695719
</Show>
696720
</div>
697721
</div>
@@ -700,15 +724,15 @@ export function ServerConnectionList(props: { controller: ReturnType<typeof useS
700724
</List>
701725

702726
<div class="shrink-0 pb-5">
703-
<Button
704-
variant="secondary"
705-
icon="plus-small"
727+
<ButtonV2
728+
variant="neutral"
729+
icon="plus"
706730
size="large"
707731
onClick={props.controller.startAdd}
708732
class="py-1.5 pl-1.5 pr-3 flex items-center gap-1.5"
709733
>
710734
{language.t("dialog.server.add.button")}
711-
</Button>
735+
</ButtonV2>
712736
</div>
713737
</div>
714738
)
@@ -736,8 +760,8 @@ export function ServerConnectionForm(props: { controller: ReturnType<typeof useS
736760
onBack={props.controller.resetForm}
737761
/>
738762
<div class="shrink-0 pb-5">
739-
<Button
740-
variant="primary"
763+
<ButtonV2
764+
variant="contrast"
741765
size="large"
742766
onClick={props.controller.submitForm}
743767
disabled={props.controller.formBusy()}
@@ -748,7 +772,7 @@ export function ServerConnectionForm(props: { controller: ReturnType<typeof useS
748772
: props.controller.isAddMode()
749773
? language.t("dialog.server.add.button")
750774
: language.t("common.save")}
751-
</Button>
775+
</ButtonV2>
752776
</div>
753777
</div>
754778
)

0 commit comments

Comments
 (0)