@@ -2,6 +2,13 @@ import { ALGORITHM_SCHEMAS, KnownAlgorithm } from '@provablehq/aleo-types';
22import { Button } from '@/components/ui/button' ;
33import { Input } from '@/components/ui/input' ;
44import { Label } from '@/components/ui/label' ;
5+ import {
6+ Select ,
7+ SelectContent ,
8+ SelectItem ,
9+ SelectTrigger ,
10+ SelectValue ,
11+ } from '@/components/ui/select' ;
512import {
613 eligibleAlgorithmsForBaseType ,
714 FormState ,
@@ -32,133 +39,153 @@ export function PrimitiveSlotEditor({
3239 const algSchema = state . derivedAlgorithm ? ALGORITHM_SCHEMAS [ state . derivedAlgorithm ] : null ;
3340
3441 return (
35- < div className = "space-y-2 border border-dashed border-border rounded-lg p-3" >
36- < Label className = "body-s-bold" >
37- { slot . name } < span className = "label-xs text-muted-foreground normal-case" > ({ slot . raw } )</ span >
38- </ Label >
39- { modes . length > 1 && (
40- < div className = "flex gap-1 flex-wrap" >
41- { modes . map ( mode => (
42- < Button
43- key = { mode }
44- type = "button"
45- size = "sm"
46- variant = { state . mode === mode ? 'default' : 'outline' }
47- onClick = { ( ) => {
48- const patch : Partial < SlotState > = { mode } ;
49- if ( mode === 'derived' && ! state . derivedAlgorithm && eligibleAlgs [ 0 ] ) {
50- ( patch as { derivedAlgorithm ?: KnownAlgorithm } ) . derivedAlgorithm =
51- eligibleAlgs [ 0 ] ;
52- }
53- updateSlot ( index , patch ) ;
54- } }
55- >
56- { mode === 'literal'
57- ? 'Literal'
58- : mode === 'address'
59- ? 'Wallet active address'
60- : 'Derived (wallet computes)' }
61- </ Button >
62- ) ) }
63- </ div >
64- ) }
65- { state . mode === 'literal' && (
66- < Input
67- placeholder = { `aleo literal (e.g. ${ slot . baseType } .${ slot . visibility === 'public' ? 'public value' : 'value' } )` }
68- value = { state . value }
69- onChange = { e => updateSlot ( index , { value : e . target . value } ) }
70- />
71- ) }
72- { state . mode === 'address' && (
73- < p className = "body-s text-muted-foreground" >
74- Sends < code > { `{type:"address"}` } </ code > . The wallet fills the slot with the active
75- address; the dapp never sees it.
76- </ p >
77- ) }
78- { state . mode === 'derived' && (
79- < div className = "space-y-2" >
80- < div className = "space-y-1" >
81- < Label className = "body-s" > Algorithm</ Label >
82- < select
83- value = { state . derivedAlgorithm }
84- onChange = { e =>
85- updateSlot ( index , {
86- derivedAlgorithm : e . target . value as KnownAlgorithm | '' ,
87- derivedArgs : { } ,
88- } )
89- }
90- className = "body-s w-full font-mono rounded-xl border border-input px-3 py-2 shadow-sm bg-background"
91- >
92- < option value = "" > - pick an algorithm -</ option >
93- { eligibleAlgs . map ( name => (
94- < option key = { name } value = { name } >
95- { name }
96- </ option >
97- ) ) }
98- </ select >
42+ < div className = "border border-border rounded-lg overflow-hidden " >
43+ { /* Slot header */ }
44+ < div className = "flex items-center gap-2 px-3 py-2 bg-muted border-b border-border" >
45+ < span className = "label-xs text-muted-foreground tabular-nums shrink-0" > #{ index + 1 } </ span >
46+ < Label className = "body-xs-bold" > { slot . name } </ Label >
47+ < code className = "label-xs font-mono bg-primary/10 px-1.5 py-0.5 rounded text-primary" >
48+ { slot . raw }
49+ </ code >
50+ </ div >
51+
52+ { /* Slot body */ }
53+ < div className = "p-3 space-y-2" >
54+ { modes . length > 1 && (
55+ < div className = "flex gap-1 flex-wrap rounded-md bg-muted/30 p-1 w-fit" >
56+ { modes . map ( mode => (
57+ < Button
58+ key = { mode }
59+ type = "button"
60+ size = "sm"
61+ variant = { state . mode === mode ? 'default' : 'ghost' }
62+ onClick = { ( ) => {
63+ const patch : Partial < SlotState > = { mode } ;
64+ if ( mode === 'derived' && ! state . derivedAlgorithm && eligibleAlgs [ 0 ] ) {
65+ ( patch as { derivedAlgorithm ?: KnownAlgorithm } ) . derivedAlgorithm =
66+ eligibleAlgs [ 0 ] ;
67+ }
68+ updateSlot ( index , patch ) ;
69+ } }
70+ >
71+ { mode === 'literal'
72+ ? 'Literal'
73+ : mode === 'address'
74+ ? 'Wallet active address'
75+ : 'Derived (wallet computes)' }
76+ </ Button >
77+ ) ) }
9978 </ div >
100- { algSchema && (
101- < div className = "space-y-2 border-l-2 border-muted-foreground/20 pl-3" >
102- < p className = "body-s text-muted-foreground" >
103- Output type: < code > { algSchema . outputType } </ code > . Sends{ ' ' }
104- < code > { `{type:"derived", algorithm, args}` } </ code > . Authorized only if a matching{ ' ' }
105- < code > algorithmsAllowed</ code > grant exists for{ ' ' }
106- < code >
107- { form . programName . trim ( ) } /{ form . functionName . trim ( ) } @{ index }
108- </ code >
109- .
110- </ p >
111- { Object . entries ( algSchema . args ) . map ( ( [ argName , rawSchema ] ) => {
112- const argSchema = rawSchema as {
113- type : string ;
114- possibleValues ?: readonly string [ ] ;
115- optional ?: boolean ;
116- } ;
117- const current = state . derivedArgs [ argName ] ?? '' ;
118- const setArg = ( value : string ) =>
79+ ) }
80+
81+ { state . mode === 'literal' && (
82+ < Input
83+ placeholder = { `aleo literal (e.g. ${ slot . baseType } .${ slot . visibility === 'public' ? 'public value' : 'value' } )` }
84+ value = { state . value }
85+ onChange = { e => updateSlot ( index , { value : e . target . value } ) }
86+ />
87+ ) }
88+
89+ { state . mode === 'address' && (
90+ < p className = "body-s text-muted-foreground" >
91+ Sends < code > { `{type:"address"}` } </ code > . The wallet fills the slot with the active
92+ address; the dapp never sees it.
93+ </ p >
94+ ) }
95+
96+ { state . mode === 'derived' && (
97+ < div className = "space-y-2" >
98+ < div className = "space-y-1" >
99+ < Label className = "body-s" > Algorithm</ Label >
100+ < Select
101+ value = { state . derivedAlgorithm || undefined }
102+ onValueChange = { value =>
119103 updateSlot ( index , {
120- derivedArgs : { ...state . derivedArgs , [ argName ] : value } ,
121- } ) ;
122- return (
123- < div key = { argName } className = "space-y-1" >
124- < Label className = "body-s" >
125- { argName } { ' ' }
126- < span className = "label-xs text-muted-foreground normal-case" >
127- ({ argSchema . type }
128- { argSchema . optional ? ', optional' : '' } )
129- </ span >
130- </ Label >
131- { argSchema . possibleValues ? (
132- < select
133- value = { current }
134- onChange = { e => setArg ( e . target . value ) }
135- className = "body-s w-full font-mono rounded-xl border border-input px-3 py-2 shadow-sm bg-background"
136- >
137- { argSchema . optional && < option value = "" > - omit -</ option > }
138- { argSchema . possibleValues . map ( value => (
139- < option key = { value } value = { value } >
140- { value }
141- </ option >
142- ) ) }
143- </ select >
144- ) : (
145- < Input
146- placeholder = {
147- argSchema . type === 'string'
148- ? `value for ${ argName } ${ argSchema . optional ? ' (optional)' : '' } `
149- : `aleo literal of type ${ argSchema . type } (e.g. "12345${ argSchema . type } ")`
150- }
151- value = { current }
152- onChange = { e => setArg ( e . target . value ) }
153- />
154- ) }
155- </ div >
156- ) ;
157- } ) }
104+ derivedAlgorithm : value as KnownAlgorithm | '' ,
105+ derivedArgs : { } ,
106+ } )
107+ }
108+ >
109+ < SelectTrigger className = "font-mono rounded-sm" >
110+ < SelectValue placeholder = "- pick an algorithm -" />
111+ </ SelectTrigger >
112+ < SelectContent >
113+ { eligibleAlgs . map ( name => (
114+ < SelectItem key = { name } value = { name } className = "font-mono" >
115+ { name }
116+ </ SelectItem >
117+ ) ) }
118+ </ SelectContent >
119+ </ Select >
158120 </ div >
159- ) }
160- </ div >
161- ) }
121+
122+ { algSchema && (
123+ < div className = "rounded-md border border-border overflow-hidden" >
124+ < div className = "px-3 py-2 bg-muted/30 border-b border-border" >
125+ < p className = "body-s text-muted-foreground" >
126+ Output: < code > { algSchema . outputType } </ code > . Sends{ ' ' }
127+ < code > { `{type:"derived", algorithm, args}` } </ code > . Needs a matching{ ' ' }
128+ < code > algorithmsAllowed</ code > grant for{ ' ' }
129+ < code >
130+ { form . programName . trim ( ) } /{ form . functionName . trim ( ) } @{ index }
131+ </ code >
132+ .
133+ </ p >
134+ </ div >
135+ < div className = "p-3 space-y-2" >
136+ { Object . entries ( algSchema . args ) . map ( ( [ argName , rawSchema ] ) => {
137+ const argSchema = rawSchema as {
138+ type : string ;
139+ possibleValues ?: readonly string [ ] ;
140+ optional ?: boolean ;
141+ } ;
142+ const current = state . derivedArgs [ argName ] ?? '' ;
143+ const setArg = ( value : string ) =>
144+ updateSlot ( index , {
145+ derivedArgs : { ...state . derivedArgs , [ argName ] : value } ,
146+ } ) ;
147+ return (
148+ < div key = { argName } className = "space-y-1" >
149+ < Label className = "body-s" >
150+ { argName } { ' ' }
151+ < span className = "label-xs text-muted-foreground normal-case" >
152+ ({ argSchema . type }
153+ { argSchema . optional ? ', optional' : '' } )
154+ </ span >
155+ </ Label >
156+ { argSchema . possibleValues ? (
157+ < Select value = { current || undefined } onValueChange = { setArg } >
158+ < SelectTrigger className = "font-mono rounded-sm" >
159+ < SelectValue placeholder = "- omit -" />
160+ </ SelectTrigger >
161+ < SelectContent >
162+ { argSchema . possibleValues . map ( value => (
163+ < SelectItem key = { value } value = { value } className = "font-mono" >
164+ { value }
165+ </ SelectItem >
166+ ) ) }
167+ </ SelectContent >
168+ </ Select >
169+ ) : (
170+ < Input
171+ placeholder = {
172+ argSchema . type === 'string'
173+ ? `value for ${ argName } ${ argSchema . optional ? ' (optional)' : '' } `
174+ : `aleo literal of type ${ argSchema . type } (e.g. "12345${ argSchema . type } ")`
175+ }
176+ value = { current }
177+ onChange = { e => setArg ( e . target . value ) }
178+ />
179+ ) }
180+ </ div >
181+ ) ;
182+ } ) }
183+ </ div >
184+ </ div >
185+ ) }
186+ </ div >
187+ ) }
188+ </ div >
162189 </ div >
163190 ) ;
164191}
0 commit comments