@@ -23,11 +23,15 @@ import { useComments } from "@/context/comments"
2323import { Button } from "@opencode-ai/ui/button"
2424import { DockShellForm , DockTray } from "@opencode-ai/ui/dock-surface"
2525import { Icon } from "@opencode-ai/ui/icon"
26+ import { ProviderIcon } from "@opencode-ai/ui/provider-icon"
2627import { Tooltip , TooltipKeybind } from "@opencode-ai/ui/tooltip"
2728import { IconButton } from "@opencode-ai/ui/icon-button"
2829import { Select } from "@opencode-ai/ui/select"
2930import { RadioGroup } from "@opencode-ai/ui/radio-group"
3031import { useDialog } from "@opencode-ai/ui/context/dialog"
32+ import { ModelSelectorPopover } from "@/components/dialog-select-model"
33+ import { DialogSelectModelUnpaid } from "@/components/dialog-select-model-unpaid"
34+ import { useProviders } from "@/hooks/use-providers"
3135import { useCommand } from "@/context/command"
3236import { Persist , persisted } from "@/utils/persist"
3337import { usePermission } from "@/context/permission"
@@ -100,6 +104,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
100104 const comments = useComments ( )
101105 const params = useParams ( )
102106 const dialog = useDialog ( )
107+ const providers = useProviders ( )
103108 const command = useCommand ( )
104109 const permission = usePermission ( )
105110 const language = useLanguage ( )
@@ -945,12 +950,21 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
945950 readClipboardImage : platform . readClipboardImage ,
946951 } )
947952
953+ const variants = createMemo ( ( ) => [ "default" , ...local . model . variant . list ( ) ] )
948954 const accepting = createMemo ( ( ) => {
949955 const id = params . id
950956 if ( ! id ) return permission . isAutoAcceptingDirectory ( sdk . directory )
951957 return permission . isAutoAccepting ( id , sdk . directory )
952958 } )
953959
960+ const flip = ( ) => {
961+ if ( ! params . id ) {
962+ permission . toggleAutoAcceptDirectory ( sdk . directory )
963+ return
964+ }
965+ permission . toggleAutoAccept ( params . id , sdk . directory )
966+ }
967+
954968 const { abort, handleSubmit } = createPromptSubmit ( {
955969 info,
956970 imageAttachments,
@@ -1176,7 +1190,11 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
11761190 onMouseDown = { ( e ) => {
11771191 const target = e . target
11781192 if ( ! ( target instanceof HTMLElement ) ) return
1179- if ( target . closest ( '[data-action="prompt-attach"], [data-action="prompt-submit"]' ) ) {
1193+ if (
1194+ target . closest (
1195+ '[data-action="prompt-attach"], [data-action="prompt-submit"], [data-action="prompt-permissions"]' ,
1196+ )
1197+ ) {
11801198 return
11811199 }
11821200 editorRef ?. focus ( )
@@ -1310,6 +1328,99 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
13101328 < Icon name = "plus" class = "size-4.5" />
13111329 </ Button >
13121330 </ TooltipKeybind >
1331+
1332+ < Show
1333+ when = { providers . paid ( ) . length > 0 }
1334+ fallback = {
1335+ < TooltipKeybind
1336+ placement = "top"
1337+ gutter = { 8 }
1338+ title = { language . t ( "command.model.choose" ) }
1339+ keybind = { command . keybind ( "model.choose" ) }
1340+ >
1341+ < Button
1342+ as = "div"
1343+ variant = "ghost"
1344+ size = "small"
1345+ class = "min-w-0 max-w-[240px] text-13-regular text-text-base group"
1346+ style = { {
1347+ opacity : buttonsSpring ( ) ,
1348+ transform : `scale(${ 0.95 + buttonsSpring ( ) * 0.05 } )` ,
1349+ filter : `blur(${ ( 1 - buttonsSpring ( ) ) * 2 } px)` ,
1350+ } }
1351+ onClick = { ( ) => dialog . show ( ( ) => < DialogSelectModelUnpaid /> ) }
1352+ >
1353+ < Show when = { local . model . current ( ) ?. provider ?. id } >
1354+ < ProviderIcon
1355+ id = { local . model . current ( ) ! . provider . id }
1356+ class = "size-4 shrink-0 opacity-40 group-hover:opacity-100 transition-opacity duration-150"
1357+ style = { { "will-change" : "opacity" , transform : "translateZ(0)" } }
1358+ />
1359+ </ Show >
1360+ < span class = "truncate" >
1361+ { local . model . current ( ) ?. name ?? language . t ( "dialog.model.select.title" ) }
1362+ </ span >
1363+ < Icon name = "chevron-down" size = "small" class = "shrink-0" />
1364+ </ Button >
1365+ </ TooltipKeybind >
1366+ }
1367+ >
1368+ < TooltipKeybind
1369+ placement = "top"
1370+ gutter = { 8 }
1371+ title = { language . t ( "command.model.choose" ) }
1372+ keybind = { command . keybind ( "model.choose" ) }
1373+ >
1374+ < ModelSelectorPopover
1375+ triggerAs = { Button }
1376+ triggerProps = { {
1377+ variant : "ghost" ,
1378+ size : "small" ,
1379+ style : {
1380+ opacity : buttonsSpring ( ) ,
1381+ transform : `scale(${ 0.95 + buttonsSpring ( ) * 0.05 } )` ,
1382+ filter : `blur(${ ( 1 - buttonsSpring ( ) ) * 2 } px)` ,
1383+ } ,
1384+ class : "min-w-0 max-w-[240px] text-13-regular text-text-base group" ,
1385+ } }
1386+ >
1387+ < Show when = { local . model . current ( ) ?. provider ?. id } >
1388+ < ProviderIcon
1389+ id = { local . model . current ( ) ! . provider . id }
1390+ class = "size-4 shrink-0 opacity-40 group-hover:opacity-100 transition-opacity duration-150"
1391+ style = { { "will-change" : "opacity" , transform : "translateZ(0)" } }
1392+ />
1393+ </ Show >
1394+ < span class = "truncate" >
1395+ { local . model . current ( ) ?. name ?? language . t ( "dialog.model.select.title" ) }
1396+ </ span >
1397+ < Icon name = "chevron-down" size = "small" class = "shrink-0" />
1398+ </ ModelSelectorPopover >
1399+ </ TooltipKeybind >
1400+ </ Show >
1401+
1402+ < TooltipKeybind
1403+ placement = "top"
1404+ gutter = { 8 }
1405+ title = { language . t ( "command.model.variant.cycle" ) }
1406+ keybind = { command . keybind ( "model.variant.cycle" ) }
1407+ >
1408+ < Select
1409+ size = "small"
1410+ options = { variants ( ) }
1411+ current = { local . model . variant . current ( ) ?? "default" }
1412+ label = { ( x ) => ( x === "default" ? language . t ( "common.default" ) : x ) }
1413+ onSelect = { ( x ) => local . model . variant . set ( x === "default" ? undefined : x ) }
1414+ class = "capitalize max-w-[160px]"
1415+ valueClass = "truncate text-13-regular text-text-base"
1416+ triggerStyle = { {
1417+ opacity : buttonsSpring ( ) ,
1418+ transform : `scale(${ 0.95 + buttonsSpring ( ) * 0.05 } )` ,
1419+ filter : `blur(${ ( 1 - buttonsSpring ( ) ) * 2 } px)` ,
1420+ } }
1421+ variant = "ghost"
1422+ />
1423+ </ TooltipKeybind >
13131424 </ div >
13141425 </ div >
13151426 </ div >
@@ -1355,6 +1466,42 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
13551466 variant = "ghost"
13561467 />
13571468 </ TooltipKeybind >
1469+
1470+ < TooltipKeybind
1471+ placement = "top"
1472+ gutter = { 4 }
1473+ title = { language . t (
1474+ accepting ( ) ? "command.permissions.autoaccept.disable" : "command.permissions.autoaccept.enable" ,
1475+ ) }
1476+ keybind = { command . keybind ( "permissions.autoaccept" ) }
1477+ >
1478+ < Select
1479+ size = "normal"
1480+ options = { [ "default" , "autoaccept" ] as const }
1481+ current = { accepting ( ) ? "autoaccept" : "default" }
1482+ label = { ( x ) =>
1483+ x === "autoaccept"
1484+ ? language . t ( "command.permissions.autoaccept.enable" )
1485+ : `${ language . t ( "common.default" ) } ${ language . t ( "command.category.permissions" ) } `
1486+ }
1487+ onSelect = { ( x ) => {
1488+ if ( ! x ) return
1489+ if ( x === "autoaccept" && accepting ( ) ) return
1490+ if ( x === "default" && ! accepting ( ) ) return
1491+ flip ( )
1492+ } }
1493+ class = "max-w-[220px]"
1494+ valueClass = "truncate text-13-regular text-text-base"
1495+ triggerStyle = { {
1496+ height : "28px" ,
1497+ opacity : buttonsSpring ( ) ,
1498+ transform : `scale(${ 0.95 + buttonsSpring ( ) * 0.05 } )` ,
1499+ filter : `blur(${ ( 1 - buttonsSpring ( ) ) * 2 } px)` ,
1500+ "pointer-events" : buttonsSpring ( ) > 0.5 ? "auto" : "none" ,
1501+ } }
1502+ variant = "ghost"
1503+ />
1504+ </ TooltipKeybind >
13581505 </ div >
13591506 </ div >
13601507 < div class = "shrink-0" >
0 commit comments