@@ -27,16 +27,24 @@ import {
2727 Hint ,
2828 Input ,
2929 type MarkKind ,
30+ PromptInfo ,
3031 Row ,
3132 Section ,
3233 Select ,
33- type SelectOption ,
3434} from "../../utils/ui/theme/index.js" ;
3535import { PhoneApprovalCallout } from "../../utils/ui/theme/PhoneApprovalCallout.js" ;
3636import { getNetworkLabel , type Env } from "../../config.js" ;
3737import { VERSION_LABEL } from "../../utils/version.js" ;
3838import type { ResolvedSigner } from "../../utils/signer.js" ;
3939import { createDevPublishSigner , type SignerMode } from "../../utils/deploy/signerMode.js" ;
40+ import {
41+ DEV_SIGNER_NO_XP_TITLE ,
42+ DEV_SIGNER_NO_XP_BODY ,
43+ shouldShowDevNoXpWarning ,
44+ } from "../deploy/signerNotice.js" ;
45+ import { SIGNER_HELP , DOMAIN_HELP , PUBLISH_HELP , TAGS_HELP } from "../deploy/promptHelp.js" ;
46+ import { PLAYGROUND_TAGS } from "../../utils/deploy/tags.js" ;
47+ import { decentralizeSignerOptions , decentralizeSignerInitialIndex } from "./signerPrompt.js" ;
4048import type { SigningEvent } from "../../utils/deploy/signingProxy.js" ;
4149import { resolveDomain } from "../../utils/decentralize/domain.js" ;
4250import { FREE_DOMAIN_SUFFIX_NOTE } from "../../utils/decentralize/randomName.js" ;
@@ -72,6 +80,11 @@ export interface DecentralizeScreenProps {
7280 * publish prompt is shown.
7381 */
7482 initialPublishToPlayground : boolean | null ;
83+ /**
84+ * Pre-set to the `--tag` value when passed (skips the tag prompt).
85+ * `undefined` means the tag prompt is shown when publishing.
86+ */
87+ initialTag ?: string ;
7588 onDone : ( result : DecentralizeResult ) => void ;
7689}
7790
@@ -82,11 +95,18 @@ export function DecentralizeScreen({
8295 explicitSigner,
8396 sessionSigner,
8497 initialPublishToPlayground,
98+ initialTag,
8599 onDone,
86100} : DecentralizeScreenProps ) {
87101 const [ siteUrl , setSiteUrl ] = useState < string | null > ( initialSiteUrl ) ;
88102 // If --suri was passed, the user has effectively pre-chosen dev.
89103 const [ signerMode , setSignerMode ] = useState < SignerMode | null > ( explicitSigner ? "dev" : null ) ;
104+ // Which signer option the cursor is on in the prompt-signer Select. Drives
105+ // the "dev signer earns no XP" warning (shown only while dev is highlighted
106+ // and a session exists). Initialised to match the default cursor position.
107+ const [ highlightedSigner , setHighlightedSigner ] = useState < SignerMode > (
108+ sessionSigner ? "phone" : "dev" ,
109+ ) ;
90110 const [ domainRaw , setDomainRaw ] = useState < string | null > ( initialDot ) ;
91111 const [ domainLabel , setDomainLabel ] = useState < string | null > ( null ) ;
92112 const [ fullDomain , setFullDomain ] = useState < string | null > ( null ) ;
@@ -97,6 +117,9 @@ export function DecentralizeScreen({
97117 const [ publishToPlayground , setPublishToPlayground ] = useState < boolean | null > (
98118 initialPublishToPlayground ,
99119 ) ;
120+ // Category tag (tri-state, mirroring deploy): `undefined` = not asked yet,
121+ // `null` = explicitly skipped, a string = chosen. Pre-filled from `--tag`.
122+ const [ tag , setTag ] = useState < string | null | undefined > ( initialTag ) ;
100123
101124 const [ stage , setStage ] = useState < Stage > ( ( ) =>
102125 pickNextStage ( {
@@ -105,6 +128,7 @@ export function DecentralizeScreen({
105128 domainLabel : null ,
106129 domainRaw : initialDot ,
107130 publishToPlayground : initialPublishToPlayground ,
131+ tag : initialTag ,
108132 } ) ,
109133 ) ;
110134
@@ -115,6 +139,7 @@ export function DecentralizeScreen({
115139 domainLabel : string | null ;
116140 domainRaw : string | null ;
117141 publishToPlayground : boolean | null ;
142+ tag : string | null ;
118143 } > = { } ,
119144 ) => {
120145 setStage (
@@ -127,6 +152,9 @@ export function DecentralizeScreen({
127152 next . publishToPlayground !== undefined
128153 ? next . publishToPlayground
129154 : publishToPlayground ,
155+ // `undefined` is a meaningful tag value ("not asked"), so detect
156+ // presence with `in` rather than the `!== undefined` sentinel.
157+ tag : "tag" in next ? next . tag : tag ,
130158 } ) ,
131159 ) ;
132160 } ;
@@ -154,7 +182,7 @@ export function DecentralizeScreen({
154182
155183 { stage . kind === "prompt-url" && (
156184 < >
157- < Callout tone = "warning " title = "About This Command" >
185+ < Callout tone = "accent " title = "About This Command" >
158186 < Text >
159187 Mirrors a live static site (https URL) and republishes it as a .dot
160188 site. Large sites can take several minutes to download — press Ctrl+C
@@ -174,37 +202,54 @@ export function DecentralizeScreen({
174202 ) }
175203
176204 { stage . kind === "prompt-signer" && (
177- < Select < SignerMode >
178- label = "signer"
179- options = { signerOptions ( sessionSigner ) }
180- onSelect = { ( mode ) => {
181- if ( mode === "phone" && ! sessionSigner ) {
182- setStage ( {
183- kind : "error" ,
184- message :
185- 'No session found — run "playground login" to log in, then re-run, or pick the dev signer.' ,
186- } ) ;
187- return ;
188- }
189- setSignerMode ( mode ) ;
190- advance ( { signerMode : mode } ) ;
191- } }
192- />
205+ < Box flexDirection = "column" >
206+ < PromptInfo box = { SIGNER_HELP } />
207+ < Select < SignerMode >
208+ label = "signer"
209+ options = { decentralizeSignerOptions ( sessionSigner != null ) }
210+ initialIndex = { decentralizeSignerInitialIndex ( sessionSigner != null ) }
211+ onHighlight = { setHighlightedSigner }
212+ onSelect = { ( mode ) => {
213+ if ( mode === "phone" && ! sessionSigner ) {
214+ setStage ( {
215+ kind : "error" ,
216+ message :
217+ 'No session found — run "playground login" to log in, then re-run, or pick the dev signer.' ,
218+ } ) ;
219+ return ;
220+ }
221+ setSignerMode ( mode ) ;
222+ advance ( { signerMode : mode } ) ;
223+ } }
224+ />
225+ { /* Below the options (mirroring the phone-approval notices)
226+ and only while the dev option is highlighted with a
227+ session present, so the "no XP" trade-off shows exactly
228+ when the user is about to pick the dev signer. */ }
229+ { shouldShowDevNoXpWarning ( sessionSigner != null , highlightedSigner ) && (
230+ < Callout tone = "warning" title = { DEV_SIGNER_NO_XP_TITLE } >
231+ < Text > { DEV_SIGNER_NO_XP_BODY } </ Text >
232+ </ Callout >
233+ ) }
234+ </ Box >
193235 ) }
194236
195237 { stage . kind === "prompt-domain" && (
196- < Input
197- label = "domain"
198- placeholder = "leave blank to auto-generate from the URL"
199- prefill = { domainRaw ?? "" }
200- externalError = { domainError }
201- validate = { validateDomainInput }
202- onSubmit = { ( value ) => {
203- setDomainError ( null ) ;
204- setDomainRaw ( value ) ;
205- advance ( { domainRaw : value } ) ;
206- } }
207- />
238+ < Box flexDirection = "column" >
239+ < PromptInfo box = { DOMAIN_HELP } />
240+ < Input
241+ label = "domain"
242+ placeholder = "leave blank to auto-generate from the URL"
243+ prefill = { domainRaw ?? "" }
244+ externalError = { domainError }
245+ validate = { validateDomainInput }
246+ onSubmit = { ( value ) => {
247+ setDomainError ( null ) ;
248+ setDomainRaw ( value ) ;
249+ advance ( { domainRaw : value } ) ;
250+ } }
251+ />
252+ </ Box >
208253 ) }
209254
210255 { stage . kind === "validate-domain" && (
@@ -235,25 +280,49 @@ export function DecentralizeScreen({
235280 ) }
236281
237282 { stage . kind === "prompt-publish" && (
238- < Select < boolean >
239- label = "publish to the playground registry?"
240- options = { [
241- {
242- value : false ,
243- label : "no" ,
244- hint : "just register the .dot name (DotNS only)" ,
245- } ,
246- {
247- value : true ,
248- label : "yes" ,
249- hint : "list the mirrored site in the playground apps tab" ,
250- } ,
251- ] }
252- onSelect = { ( choice ) => {
253- setPublishToPlayground ( choice ) ;
254- advance ( { publishToPlayground : choice } ) ;
255- } }
256- />
283+ < Box flexDirection = "column" >
284+ < PromptInfo box = { PUBLISH_HELP } />
285+ < Select < boolean >
286+ label = "publish to the playground registry?"
287+ options = { [
288+ {
289+ value : true ,
290+ label : "yes" ,
291+ hint : "list the mirrored site in the playground apps tab" ,
292+ } ,
293+ {
294+ value : false ,
295+ label : "no" ,
296+ hint : "just register the .dot name (DotNS only)" ,
297+ } ,
298+ ] }
299+ initialIndex = { 0 }
300+ onSelect = { ( choice ) => {
301+ setPublishToPlayground ( choice ) ;
302+ advance ( { publishToPlayground : choice } ) ;
303+ } }
304+ />
305+ </ Box >
306+ ) }
307+
308+ { stage . kind === "prompt-tags" && (
309+ < Box flexDirection = "column" >
310+ < PromptInfo box = { TAGS_HELP } />
311+ < Select < string | null >
312+ label = "tag this app?"
313+ options = { [
314+ ...PLAYGROUND_TAGS . map ( ( t ) => ( {
315+ value : t as string | null ,
316+ label : t ,
317+ } ) ) ,
318+ { value : null , label : "skip" , hint : "publish without a tag" } ,
319+ ] }
320+ onSelect = { ( t ) => {
321+ setTag ( t ) ;
322+ advance ( { tag : t } ) ;
323+ } }
324+ />
325+ </ Box >
257326 ) }
258327
259328 { stage . kind === "confirm" && (
@@ -265,6 +334,7 @@ export function DecentralizeScreen({
265334 signer = { activeSigner ! }
266335 signerMode = { signerMode ! }
267336 publishToPlayground = { publishToPlayground === true }
337+ tag = { publishToPlayground === true ? ( tag ?? null ) : null }
268338 onConfirm = { ( ) => setStage ( { kind : "running" } ) }
269339 onCancel = { ( ) => onDone ( { kind : "cancel" } ) }
270340 />
@@ -278,6 +348,7 @@ export function DecentralizeScreen({
278348 mode = { signerMode ! }
279349 userSigner = { explicitSigner ?? sessionSigner }
280350 publishToPlayground = { publishToPlayground === true }
351+ tag = { publishToPlayground === true ? ( tag ?? null ) : null }
281352 env = { env }
282353 onComplete = { ( outcome ) => setStage ( { kind : "done" , outcome } ) }
283354 onFailed = { ( message ) => setStage ( { kind : "error" , message } ) }
@@ -301,23 +372,6 @@ export function DecentralizeScreen({
301372 ) ;
302373}
303374
304- function signerOptions ( sessionSigner : ResolvedSigner | null ) : SelectOption < SignerMode > [ ] {
305- return [
306- {
307- value : "dev" ,
308- label : "dev signer" ,
309- hint : "fast, signs locally with the polkadot-app-deploy default account" ,
310- } ,
311- {
312- value : "phone" ,
313- label : "your phone signer" ,
314- hint : sessionSigner
315- ? "signed with your logged-in account"
316- : "requires `playground login` first" ,
317- } ,
318- ] ;
319- }
320-
321375// ── Validate-domain stage ────────────────────────────────────────────────────
322376
323377function ValidateDomainStage ( {
@@ -390,6 +444,7 @@ function ConfirmStage({
390444 signer,
391445 signerMode,
392446 publishToPlayground,
447+ tag,
393448 onConfirm,
394449 onCancel,
395450} : {
@@ -400,6 +455,7 @@ function ConfirmStage({
400455 signer : ResolvedSigner ;
401456 signerMode : SignerMode ;
402457 publishToPlayground : boolean ;
458+ tag : string | null ;
403459 onConfirm : ( ) => void ;
404460 onCancel : ( ) => void ;
405461} ) {
@@ -418,6 +474,12 @@ function ConfirmStage({
418474 value = { publishToPlayground ? "publish to apps tab" : "skip" }
419475 tone = { publishToPlayground ? "accent" : "muted" }
420476 />
477+ { /* Surface the chosen tag before the irreversible publish, like
478+ deploy's confirm summary. Only shown when publishing — the
479+ tag is otherwise irrelevant. */ }
480+ { publishToPlayground && (
481+ < Row label = "tag" value = { tag ?? "none" } tone = { tag ? "accent" : "muted" } />
482+ ) }
421483 { availabilityNote && < Row label = "note" value = { availabilityNote } tone = "warning" /> }
422484 </ Section >
423485 { autoGenerated && < Hint indent = { 2 } > { FREE_DOMAIN_SUFFIX_NOTE } </ Hint > }
@@ -461,6 +523,7 @@ function RunningStage({
461523 mode,
462524 userSigner,
463525 publishToPlayground,
526+ tag,
464527 env,
465528 onComplete,
466529 onFailed,
@@ -471,6 +534,7 @@ function RunningStage({
471534 mode : SignerMode ;
472535 userSigner : ResolvedSigner | null ;
473536 publishToPlayground : boolean ;
537+ tag : string | null ;
474538 env : Env ;
475539 onComplete : ( outcome : DecentralizeOutcome ) => void ;
476540 onFailed : ( message : string ) => void ;
@@ -516,6 +580,7 @@ function RunningStage({
516580 mode,
517581 userSigner,
518582 publishToPlayground,
583+ tag,
519584 env,
520585 onEvent : ( event ) => {
521586 switch ( event . kind ) {
0 commit comments