3434from reflex import constants , model
3535from reflex .compiler import templates
3636from reflex .config import Config , environment , get_config
37- from reflex .utils import console , net , path_ops , processes
37+ from reflex .utils import console , net , path_ops , processes , redir
3838from reflex .utils .exceptions import (
3939 GeneratedCodeHasNoFunctionDefs ,
4040 raise_system_package_missing_error ,
@@ -1211,7 +1211,7 @@ def check_schema_up_to_date():
12111211 )
12121212
12131213
1214- def prompt_for_template (templates : list [Template ]) -> str :
1214+ def prompt_for_template_options (templates : list [Template ]) -> str :
12151215 """Prompt the user to specify a template.
12161216
12171217 Args:
@@ -1223,9 +1223,14 @@ def prompt_for_template(templates: list[Template]) -> str:
12231223 # Show the user the URLs of each template to preview.
12241224 console .print ("\n Get started with a template:" )
12251225
1226+ def format_demo_url_str (url : str ) -> str :
1227+ return f" ({ url } )" if url else ""
1228+
12261229 # Prompt the user to select a template.
12271230 id_to_name = {
1228- str (idx ): f"{ template .name } ({ template .demo_url } ) - { template .description } "
1231+ str (
1232+ idx
1233+ ): f"{ template .name .replace ('_' , ' ' ).replace ('-' , ' ' )} { format_demo_url_str (template .demo_url )} - { template .description } "
12291234 for idx , template in enumerate (templates )
12301235 }
12311236 for id in range (len (id_to_name )):
@@ -1380,15 +1385,119 @@ def create_config_init_app_from_remote_template(app_name: str, template_url: str
13801385 shutil .rmtree (unzip_dir )
13811386
13821387
1383- def initialize_app (app_name : str , template : str | None = None ) -> str | None :
1384- """Initialize the app either from a remote template or a blank app. If the config file exists, it is considered as reinit .
1388+ def initialize_default_app (app_name : str ) :
1389+ """Initialize the default app.
13851390
13861391 Args:
13871392 app_name: The name of the app.
1388- template: The name of the template to use.
1393+ """
1394+ create_config (app_name )
1395+ initialize_app_directory (app_name )
1396+
1397+
1398+ def validate_and_create_app_using_remote_template (app_name , template , templates ):
1399+ """Validate and create an app using a remote template.
1400+
1401+ Args:
1402+ app_name: The name of the app.
1403+ template: The name of the template.
1404+ templates: The available templates.
1405+
1406+ Raises:
1407+ Exit: If the template is not found.
1408+ """
1409+ # If user selects a template, it needs to exist.
1410+ if template in templates :
1411+ template_url = templates [template ].code_url
1412+ else :
1413+ # Check if the template is a github repo.
1414+ if template .startswith ("https://github.com" ):
1415+ template_url = f"{ template .strip ('/' ).replace ('.git' , '' )} /archive/main.zip"
1416+ else :
1417+ console .error (f"Template `{ template } ` not found." )
1418+ raise typer .Exit (1 )
1419+
1420+ if template_url is None :
1421+ return
1422+
1423+ create_config_init_app_from_remote_template (
1424+ app_name = app_name , template_url = template_url
1425+ )
1426+
1427+
1428+ def generate_template_using_ai (template : str | None = None ) -> str :
1429+ """Generate a template using AI(Flexgen).
1430+
1431+ Args:
1432+ template: The name of the template.
1433+
1434+ Returns:
1435+ The generation hash.
13891436
13901437 Raises:
1391- Exit: If template is directly provided in the command flag and is invalid.
1438+ Exit: If the template and ai flags are used.
1439+ """
1440+ if template is None :
1441+ # If AI is requested and no template specified, redirect the user to reflex.build.
1442+ return redir .reflex_build_redirect ()
1443+ elif is_generation_hash (template ):
1444+ # Otherwise treat the template as a generation hash.
1445+ return template
1446+ else :
1447+ console .error (
1448+ "Cannot use `--template` option with `--ai` option. Please remove `--template` option."
1449+ )
1450+ raise typer .Exit (2 )
1451+
1452+
1453+ def fetch_remote_templates (
1454+ template : Optional [str ] = None ,
1455+ ) -> tuple [str , dict [str , Template ]]:
1456+ """Fetch the available remote templates.
1457+
1458+ Args:
1459+ template: The name of the template.
1460+
1461+ Returns:
1462+ The selected template and the available templates.
1463+
1464+ Raises:
1465+ Exit: If the template is not valid or if the template is not specified.
1466+ """
1467+ available_templates = {}
1468+
1469+ try :
1470+ # Get the available templates
1471+ available_templates = fetch_app_templates (constants .Reflex .VERSION )
1472+ except Exception as e :
1473+ console .warn ("Failed to fetch templates. Falling back to default template." )
1474+ console .debug (f"Error while fetching templates: { e } " )
1475+ template = constants .Templates .DEFAULT
1476+
1477+ if template == constants .Templates .DEFAULT :
1478+ return template , available_templates
1479+
1480+ if template in available_templates :
1481+ return template , available_templates
1482+
1483+ else :
1484+ if template is not None :
1485+ console .error (f"{ template !r} is not a valid template name." )
1486+ console .print (
1487+ f"Go to the templates page ({ constants .Templates .REFLEX_TEMPLATES_URL } ) and copy the command to init with a template."
1488+ )
1489+ raise typer .Exit (0 )
1490+
1491+
1492+ def initialize_app (
1493+ app_name : str , template : str | None = None , ai : bool = False
1494+ ) -> str | None :
1495+ """Initialize the app either from a remote template or a blank app. If the config file exists, it is considered as reinit.
1496+
1497+ Args:
1498+ app_name: The name of the app.
1499+ template: The name of the template to use.
1500+ ai: Whether to use AI to generate the template.
13921501
13931502 Returns:
13941503 The name of the template.
@@ -1401,54 +1510,73 @@ def initialize_app(app_name: str, template: str | None = None) -> str | None:
14011510 telemetry .send ("reinit" )
14021511 return
14031512
1513+ generation_hash = None
1514+ if ai :
1515+ generation_hash = generate_template_using_ai (template )
1516+ template = constants .Templates .DEFAULT
1517+
14041518 templates : dict [str , Template ] = {}
14051519
14061520 # Don't fetch app templates if the user directly asked for DEFAULT.
1407- if template is None or (template != constants .Templates .DEFAULT ):
1408- try :
1409- # Get the available templates
1410- templates = fetch_app_templates ( constants . Reflex . VERSION )
1411- if template is None and len ( templates ) > 0 :
1412- template = prompt_for_template ( list ( templates . values ()))
1413- except Exception as e :
1414- console . warn ( "Failed to fetch templates. Falling back to default template." )
1415- console . debug ( f"Error while fetching templates: { e } " )
1416- finally :
1417- template = template or constants . Templates . DEFAULT
1521+ if template is not None and (template not in ( constants .Templates .DEFAULT ,) ):
1522+ template , templates = fetch_remote_templates ( template )
1523+
1524+ if template is None :
1525+ template = prompt_for_template_options ( get_init_cli_prompt_options ())
1526+ if template == constants . Templates . AI :
1527+ generation_hash = generate_template_using_ai ()
1528+ # change to the default to allow creation of default app
1529+ template = constants . Templates . DEFAULT
1530+ elif template == constants . Templates . CHOOSE_TEMPLATES :
1531+ template , templates = fetch_remote_templates ()
14181532
14191533 # If the blank template is selected, create a blank app.
1420- if template == constants .Templates .DEFAULT :
1534+ if template in ( constants .Templates .DEFAULT ,) :
14211535 # Default app creation behavior: a blank app.
1422- create_config (app_name )
1423- initialize_app_directory (app_name )
1536+ initialize_default_app (app_name )
14241537 else :
1425- # Fetch App templates from the backend server.
1426- console .debug (f"Available templates: { templates } " )
1427-
1428- # If user selects a template, it needs to exist.
1429- if template in templates :
1430- template_url = templates [template ].code_url
1431- else :
1432- # Check if the template is a github repo.
1433- if template .startswith ("https://github.com" ):
1434- template_url = (
1435- f"{ template .strip ('/' ).replace ('.git' , '' )} /archive/main.zip"
1436- )
1437- else :
1438- console .error (f"Template `{ template } ` not found." )
1439- raise typer .Exit (1 )
1440-
1441- if template_url is None :
1442- return
1443-
1444- create_config_init_app_from_remote_template (
1445- app_name = app_name , template_url = template_url
1538+ validate_and_create_app_using_remote_template (
1539+ app_name = app_name , template = template , templates = templates
14461540 )
14471541
1542+ # If a reflex.build generation hash is available, download the code and apply it to the main module.
1543+ if generation_hash :
1544+ initialize_main_module_index_from_generation (
1545+ app_name , generation_hash = generation_hash
1546+ )
14481547 telemetry .send ("init" , template = template )
1548+
14491549 return template
14501550
14511551
1552+ def get_init_cli_prompt_options () -> list [Template ]:
1553+ """Get the CLI options for initializing a Reflex app.
1554+
1555+ Returns:
1556+ The CLI options.
1557+ """
1558+ return [
1559+ Template (
1560+ name = constants .Templates .DEFAULT ,
1561+ description = "A blank Reflex app." ,
1562+ demo_url = constants .Templates .DEFAULT_TEMPLATE_URL ,
1563+ code_url = "" ,
1564+ ),
1565+ Template (
1566+ name = constants .Templates .AI ,
1567+ description = "Generate a template using AI [Experimental]" ,
1568+ demo_url = "" ,
1569+ code_url = "" ,
1570+ ),
1571+ Template (
1572+ name = constants .Templates .CHOOSE_TEMPLATES ,
1573+ description = "Choose an existing template." ,
1574+ demo_url = "" ,
1575+ code_url = "" ,
1576+ ),
1577+ ]
1578+
1579+
14521580def initialize_main_module_index_from_generation (app_name : str , generation_hash : str ):
14531581 """Overwrite the `index` function in the main module with reflex.build generated code.
14541582
0 commit comments