From 8133aa10c9894da2faa96c537cbc9947793e52c7 Mon Sep 17 00:00:00 2001 From: Nikhil Rao Date: Thu, 26 Oct 2023 17:59:14 -0700 Subject: [PATCH] Update base template (#2027) --- reflex/.templates/apps/base/README.md | 72 ++++++++++++ reflex/.templates/apps/base/code/base.py | 105 ++---------------- .../code => base/code/components}/__init__.py | 0 .../base/code/{ => components}/sidebar.py | 82 ++++++++------ .../apps/base/code/pages/__init__.py | 7 +- .../apps/base/code/pages/dashboard.py | 33 +++--- .../.templates/apps/base/code/pages/home.py | 28 ----- .../.templates/apps/base/code/pages/index.py | 18 +++ .../apps/base/code/pages/settings.py | 34 +++--- reflex/.templates/apps/base/code/state.py | 10 +- reflex/.templates/apps/base/code/styles.py | 21 +++- .../apps/base/code/templates/__init__.py | 1 + .../apps/base/code/templates/template.py | 99 +++++++++++++++++ .../{default => blank}/assets/favicon.ico | Bin reflex/.templates/apps/blank/code/__init__.py | 0 .../code/default.py => blank/code/blank.py} | 0 reflex/components/typography/markdown.py | 2 +- reflex/constants/base.py | 2 + reflex/page.py | 12 ++ reflex/reflex.py | 2 +- reflex/testing.py | 2 +- reflex/utils/exec.py | 2 +- reflex/utils/path_ops.py | 19 ++++ reflex/utils/prerequisites.py | 24 ++-- 24 files changed, 346 insertions(+), 229 deletions(-) create mode 100644 reflex/.templates/apps/base/README.md rename reflex/.templates/apps/{default/code => base/code/components}/__init__.py (100%) rename reflex/.templates/apps/base/code/{ => components}/sidebar.py (62%) delete mode 100644 reflex/.templates/apps/base/code/pages/home.py create mode 100644 reflex/.templates/apps/base/code/pages/index.py create mode 100644 reflex/.templates/apps/base/code/templates/__init__.py create mode 100644 reflex/.templates/apps/base/code/templates/template.py rename reflex/.templates/apps/{default => blank}/assets/favicon.ico (100%) create mode 100644 reflex/.templates/apps/blank/code/__init__.py rename reflex/.templates/apps/{default/code/default.py => blank/code/blank.py} (100%) diff --git a/reflex/.templates/apps/base/README.md b/reflex/.templates/apps/base/README.md new file mode 100644 index 000000000..7181696aa --- /dev/null +++ b/reflex/.templates/apps/base/README.md @@ -0,0 +1,72 @@ +# Welcome to Reflex! + +This is the base Reflex template - installed when you run `reflex init`. + +If you want to use a different template, pass the `--template` flag to `reflex init`. +For example, if you want a more basic starting point, you can run: + +```bash +reflex init --template blank +``` + +## About this Template + +This template has the following directory structure: + +```bash +. +├── assets +├── requirements.txt +├── rxconfig.py +└── {your_app} + ├── __init__.py + ├── components + │ └── sidebar.py + ├── pages + │ ├── __init__.py + │ ├── dashboard.py + │ ├── index.py + │ ├── settings.py + │ └── template.py + ├── state.py + ├── styles.py + └── {your_app}.py + +``` + +See the [Project Structure docs](https://reflex.dev/docs/getting-started/project-structure/) for more information on general Reflex project structure. + +### Adding Pages + +In this template, the pages in your app are defined in `{your_app}/pages/`. +Each page is a function that returns a Reflex component. +For example, to edit this page you can modify `{your_app}/pages/index.py`. +See the [pages docs](https://reflex.dev/docs/components/pages/) for more information on pages. + +In this template, instead of using `rx.add_page` or the `@rx.page` decorator, +we use the `@template` decorator from `{your_app}/pages/template.py`. + +To add a new page: + +1. Add a new file in `{your_app}/pages/`. We recommend using one file per page, but you can also group pages in a single file. +2. Add a new function with the `@template` decorator, which takes the same arguments as `@rx.page`. +3. Import the page in your `{your_app}/{your_app}.py` file and it will automatically be added to the app. + + +### Adding Components + +In order to keep your code organized, we recommend putting components that are +used across multiple pages in the `{your_app}/components/` directory. + +In this template, we have a sidebar component in `{your_app}/components/sidebar.py`. + +### Adding State + +In this template, we define the base state of the app in `{your_app}/state.py`. +The base state is useful for general app state that is used across multiple pages. + +In this template, the base state handles the toggle for the sidebar. + +As your app grows, we recommend using [substates](https://reflex.dev/docs/state/substates/) +to organize your state. You can either define substates in their own files, or if the state is +specific to a page, you can define it in the page file itself. diff --git a/reflex/.templates/apps/base/code/base.py b/reflex/.templates/apps/base/code/base.py index 7b4f120a0..8fde62ca4 100644 --- a/reflex/.templates/apps/base/code/base.py +++ b/reflex/.templates/apps/base/code/base.py @@ -1,101 +1,12 @@ -"""Welcome to Reflex! This file outlines the steps to create a basic app.""" -from typing import Callable +"""Welcome to Reflex!.""" + +from code import styles + +# Import all the pages. +from code.pages import * import reflex as rx -from .pages import dashboard_page, home_page, settings_page -from .sidebar import sidebar -from .state import State -from .styles import * - -meta = [ - { - "name": "viewport", - "content": "width=device-width, shrink-to-fit=no, initial-scale=1", - }, -] - - -def template(main_content: Callable[[], rx.Component]) -> rx.Component: - """The template for each page of the app. - - Args: - main_content (Callable[[], rx.Component]): The main content of the page. - - Returns: - rx.Component: The template for each page of the app. - """ - menu_button = rx.box( - rx.menu( - rx.menu_button( - rx.icon( - tag="hamburger", - size="4em", - color=text_color, - ), - ), - rx.menu_list( - rx.menu_item(rx.link("Home", href="/", width="100%")), - rx.menu_divider(), - rx.menu_item( - rx.link("About", href="https://github.com/reflex-dev", width="100%") - ), - rx.menu_item( - rx.link("Contact", href="mailto:founders@=reflex.dev", width="100%") - ), - ), - ), - position="fixed", - right="1.5em", - top="1.5em", - z_index="500", - ) - - return rx.hstack( - sidebar(), - main_content(), - rx.spacer(), - menu_button, - align_items="flex-start", - transition="left 0.5s, width 0.5s", - position="relative", - left=rx.cond(State.sidebar_displayed, "0px", f"-{sidebar_width}"), - ) - - -@rx.page("/", meta=meta) -@template -def home() -> rx.Component: - """Home page. - - Returns: - rx.Component: The home page. - """ - return home_page() - - -@rx.page("/settings", meta=meta) -@template -def settings() -> rx.Component: - """Settings page. - - Returns: - rx.Component: The settings page. - """ - return settings_page() - - -@rx.page("/dashboard", meta=meta) -@template -def dashboard() -> rx.Component: - """Dashboard page. - - Returns: - rx.Component: The dashboard page. - """ - return dashboard_page() - - -# Add state and page to the app. -app = rx.App(style=base_style) +# Create the app and compile it. +app = rx.App(style=styles.base_style) app.compile() diff --git a/reflex/.templates/apps/default/code/__init__.py b/reflex/.templates/apps/base/code/components/__init__.py similarity index 100% rename from reflex/.templates/apps/default/code/__init__.py rename to reflex/.templates/apps/base/code/components/__init__.py diff --git a/reflex/.templates/apps/base/code/sidebar.py b/reflex/.templates/apps/base/code/components/sidebar.py similarity index 62% rename from reflex/.templates/apps/base/code/sidebar.py rename to reflex/.templates/apps/base/code/components/sidebar.py index ba8e58db9..fc0f13f9b 100644 --- a/reflex/.templates/apps/base/code/sidebar.py +++ b/reflex/.templates/apps/base/code/components/sidebar.py @@ -1,23 +1,25 @@ """Sidebar component for the app.""" -import reflex as rx +from code import styles +from code.state import State -from .state import State -from .styles import * +import reflex as rx def sidebar_header() -> rx.Component: """Sidebar header. Returns: - rx.Component: The sidebar header component. + The sidebar header component. """ return rx.hstack( + # The logo. rx.image( src="/icon.svg", height="2em", ), rx.spacer(), + # Link to Reflex GitHub repo. rx.link( rx.center( rx.image( @@ -25,17 +27,17 @@ def sidebar_header() -> rx.Component: height="3em", padding="0.5em", ), - box_shadow=box_shadow, + box_shadow=styles.box_shadow, bg="transparent", - border_radius=border_radius, + border_radius=styles.border_radius, _hover={ - "bg": accent_color, + "bg": styles.accent_color, }, ), href="https://github.com/reflex-dev/reflex", ), width="100%", - border_bottom=border, + border_bottom=styles.border, padding="1em", ) @@ -44,7 +46,7 @@ def sidebar_footer() -> rx.Component: """Sidebar footer. Returns: - rx.Component: The sidebar footer component. + The sidebar footer component. """ return rx.hstack( rx.link( @@ -55,15 +57,15 @@ def sidebar_footer() -> rx.Component: padding="0.5em", ), bg="transparent", - border_radius=border_radius, - **hover_accent_bg, + border_radius=styles.border_radius, + **styles.hover_accent_bg, ), on_click=State.toggle_sidebar_displayed, transform=rx.cond(~State.sidebar_displayed, "rotate(180deg)", ""), transition="transform 0.5s, left 0.5s", position="relative", left=rx.cond(State.sidebar_displayed, "0px", "20.5em"), - **overlapping_button_style, + **styles.overlapping_button_style, ), rx.spacer(), rx.link( @@ -79,7 +81,7 @@ def sidebar_footer() -> rx.Component: href="https://reflex.dev/blog/", ), width="100%", - border_top=border, + border_top=styles.border, padding="1em", ) @@ -88,13 +90,18 @@ def sidebar_item(text: str, icon: str, url: str) -> rx.Component: """Sidebar item. Args: - text (str): The text of the item. - icon (str): The icon of the item. - url (str): The URL of the item. + text: The text of the item. + icon: The icon of the item. + url: The URL of the item. Returns: rx.Component: The sidebar item component. """ + # Whether the item is active. + active = (State.router.page.path == f"/{text.lower()}") | ( + (State.router.page.path == "/") & text == "Home" + ) + return rx.link( rx.hstack( rx.image( @@ -106,17 +113,17 @@ def sidebar_item(text: str, icon: str, url: str) -> rx.Component: text, ), bg=rx.cond( - State.origin_url == f"/{text.lower()}/", - accent_color, + active, + styles.accent_color, "transparent", ), color=rx.cond( - State.origin_url == f"/{text.lower()}/", - accent_text_color, - text_color, + active, + styles.accent_text_color, + styles.text_color, ), - border_radius=border_radius, - box_shadow=box_shadow, + border_radius=styles.border_radius, + box_shadow=styles.box_shadow, width="100%", padding_x="1em", ), @@ -126,25 +133,26 @@ def sidebar_item(text: str, icon: str, url: str) -> rx.Component: def sidebar() -> rx.Component: - """Sidebar. + """The sidebar. Returns: - rx.Component: The sidebar component. + The sidebar component. """ + # Get all the decorated pages and add them to the sidebar. + from reflex.page import get_decorated_pages + return rx.box( rx.vstack( sidebar_header(), rx.vstack( - sidebar_item( - "Dashboard", - "/github.svg", - "/dashboard", - ), - sidebar_item( - "Settings", - "/github.svg", - "/settings", - ), + *[ + sidebar_item( + text=page.get("title", page["route"].strip("/").capitalize()), + icon=page.get("image", "/github.svg"), + url=page["route"], + ) + for page in get_decorated_pages() + ], width="100%", overflow_y="auto", align_items="flex-start", @@ -154,9 +162,9 @@ def sidebar() -> rx.Component: sidebar_footer(), height="100dvh", ), - min_width=sidebar_width, + min_width=styles.sidebar_width, height="100%", position="sticky", top="0px", - border_right=border, + border_right=styles.border, ) diff --git a/reflex/.templates/apps/base/code/pages/__init__.py b/reflex/.templates/apps/base/code/pages/__init__.py index b8bb99da9..8e5da1a38 100644 --- a/reflex/.templates/apps/base/code/pages/__init__.py +++ b/reflex/.templates/apps/base/code/pages/__init__.py @@ -1,4 +1,3 @@ -"""The pages of the app.""" -from .dashboard import dashboard_page -from .home import home_page -from .settings import settings_page +from .dashboard import dashboard +from .index import index +from .settings import settings diff --git a/reflex/.templates/apps/base/code/pages/dashboard.py b/reflex/.templates/apps/base/code/pages/dashboard.py index e5c9052f5..e0a895009 100644 --- a/reflex/.templates/apps/base/code/pages/dashboard.py +++ b/reflex/.templates/apps/base/code/pages/dashboard.py @@ -1,28 +1,21 @@ -"""The dashboard page for the template.""" +"""The dashboard page.""" +from code.templates import template + import reflex as rx -from ..styles import * - -def dashboard_page() -> rx.Component: - """The UI for the dashboard page. +@template(route="/dashboard", title="Dashboard") +def dashboard() -> rx.Component: + """The dashboard page. Returns: - rx.Component: The UI for the dashboard page. + The UI for the dashboard page. """ - return rx.box( - rx.vstack( - rx.heading( - "Dashboard", - font_size="3em", - ), - rx.text( - "Welcome to Reflex!", - ), - rx.text( - "You can use this template to get started with Reflex.", - ), - style=template_content_style, + return rx.vstack( + rx.heading("Dashboard", font_size="3em"), + rx.text("Welcome to Reflex!"), + rx.text( + "You can edit this page in ", + rx.code("{your_app}/pages/dashboard.py"), ), - style=template_page_style, ) diff --git a/reflex/.templates/apps/base/code/pages/home.py b/reflex/.templates/apps/base/code/pages/home.py deleted file mode 100644 index 71f8bffde..000000000 --- a/reflex/.templates/apps/base/code/pages/home.py +++ /dev/null @@ -1,28 +0,0 @@ -"""The home page of the app.""" -import reflex as rx - -from ..styles import * - - -def home_page() -> rx.Component: - """The UI for the home page. - - Returns: - rx.Component: The UI for the home page. - """ - return rx.box( - rx.vstack( - rx.heading( - "Home", - font_size="3em", - ), - rx.text( - "Welcome to Reflex!", - ), - rx.text( - "You can use this template to get started with Reflex.", - ), - style=template_content_style, - ), - style=template_page_style, - ) diff --git a/reflex/.templates/apps/base/code/pages/index.py b/reflex/.templates/apps/base/code/pages/index.py new file mode 100644 index 000000000..af0949d76 --- /dev/null +++ b/reflex/.templates/apps/base/code/pages/index.py @@ -0,0 +1,18 @@ +"""The home page of the app.""" + +from code import styles +from code.templates import template + +import reflex as rx + + +@template(route="/", title="Home", image="/logo.svg") +def index() -> rx.Component: + """The home page. + + Returns: + The UI for the home page. + """ + with open("README.md") as readme: + content = readme.read() + return rx.markdown(content, component_map=styles.markdown_style) diff --git a/reflex/.templates/apps/base/code/pages/settings.py b/reflex/.templates/apps/base/code/pages/settings.py index 8945c65a4..7bee3bf86 100644 --- a/reflex/.templates/apps/base/code/pages/settings.py +++ b/reflex/.templates/apps/base/code/pages/settings.py @@ -1,28 +1,22 @@ -"""The settings page for the template.""" +"""The settings page.""" + +from code.templates import template + import reflex as rx -from ..styles import * - -def settings_page() -> rx.Component: - """The UI for the settings page. +@template(route="/settings", title="Settings") +def settings() -> rx.Component: + """The settings page. Returns: - rx.Component: The UI for the settings page. + The UI for the settings page. """ - return rx.box( - rx.vstack( - rx.heading( - "Settings", - font_size="3em", - ), - rx.text( - "Welcome to Reflex!", - ), - rx.text( - "You can use this template to get started with Reflex.", - ), - style=template_content_style, + return rx.vstack( + rx.heading("Settings", font_size="3em"), + rx.text("Welcome to Reflex!"), + rx.text( + "You can edit this page in ", + rx.code("{your_app}/pages/settings.py"), ), - style=template_page_style, ) diff --git a/reflex/.templates/apps/base/code/state.py b/reflex/.templates/apps/base/code/state.py index a5c6f57bd..e3df11c7a 100644 --- a/reflex/.templates/apps/base/code/state.py +++ b/reflex/.templates/apps/base/code/state.py @@ -6,17 +6,9 @@ import reflex as rx class State(rx.State): """State for the app.""" + # Whether the sidebar is displayed. sidebar_displayed: bool = True - @rx.var - def origin_url(self) -> str: - """Get the url of the current page. - - Returns: - str: The url of the current page. - """ - return self.router_data.get("asPath", "") - def toggle_sidebar_displayed(self) -> None: """Toggle the sidebar displayed.""" self.sidebar_displayed = not self.sidebar_displayed diff --git a/reflex/.templates/apps/base/code/styles.py b/reflex/.templates/apps/base/code/styles.py index 1cf62ff24..1b53f611b 100644 --- a/reflex/.templates/apps/base/code/styles.py +++ b/reflex/.templates/apps/base/code/styles.py @@ -1,7 +1,7 @@ """Styles for the app.""" -import reflex as rx +from code.state import State -from .state import State +import reflex as rx border_radius = "0.375rem" box_shadow = "0px 0px 0px 1px rgba(84, 82, 95, 0.14)" @@ -53,3 +53,20 @@ base_style = { }, rx.MenuItem: hover_accent_bg, } + +markdown_style = { + "code": lambda text: rx.code(text, color="#1F1944", bg="#EAE4FD"), + "a": lambda text, **props: rx.link( + text, + **props, + font_weight="bold", + color="#03030B", + text_decoration="underline", + text_decoration_color="#AD9BF8", + _hover={ + "color": "#AD9BF8", + "text_decoration": "underline", + "text_decoration_color": "#03030B", + }, + ), +} diff --git a/reflex/.templates/apps/base/code/templates/__init__.py b/reflex/.templates/apps/base/code/templates/__init__.py new file mode 100644 index 000000000..36f9d8423 --- /dev/null +++ b/reflex/.templates/apps/base/code/templates/__init__.py @@ -0,0 +1 @@ +from .template import template diff --git a/reflex/.templates/apps/base/code/templates/template.py b/reflex/.templates/apps/base/code/templates/template.py new file mode 100644 index 000000000..93a3296d0 --- /dev/null +++ b/reflex/.templates/apps/base/code/templates/template.py @@ -0,0 +1,99 @@ +"""Common templates used between pages in the app.""" + +from code import styles +from code.components.sidebar import sidebar +from code.state import State +from typing import Callable + +import reflex as rx + +# Meta tags for the app. +default_meta = [ + { + "name": "viewport", + "content": "width=device-width, shrink-to-fit=no, initial-scale=1", + }, +] + + +def menu_button() -> rx.Component: + """The menu button on the top right of the page. + + Returns: + The menu button component. + """ + return rx.box( + rx.menu( + rx.menu_button( + rx.icon( + tag="hamburger", + size="4em", + color=styles.text_color, + ), + ), + rx.menu_list( + rx.menu_item(rx.link("Home", href="/", width="100%")), + rx.menu_divider(), + rx.menu_item( + rx.link("About", href="https://github.com/reflex-dev", width="100%") + ), + rx.menu_item( + rx.link("Contact", href="mailto:founders@=reflex.dev", width="100%") + ), + ), + ), + position="fixed", + right="1.5em", + top="1.5em", + z_index="500", + ) + + +def template( + **page_kwargs: dict, +) -> Callable[[Callable[[], rx.Component]], rx.Component]: + """The template for each page of the app. + + Args: + page_kwargs: Keyword arguments to pass to the page. + + Returns: + The template with the page content. + """ + + def decorator(page_content: Callable[[], rx.Component]) -> rx.Component: + """The template for each page of the app. + + Args: + page_content: The content of the page. + + Returns: + The template with the page content. + """ + # Get the meta tags for the page. + page_kwargs["meta"] = [*default_meta, *page_kwargs.get("meta", [])] + + @rx.page(**page_kwargs) + def templated_page(): + return rx.hstack( + sidebar(), + rx.box( + rx.box( + page_content(), + **styles.template_content_style, + ), + **styles.template_page_style, + ), + rx.spacer(), + menu_button(), + align_items="flex-start", + transition="left 0.5s, width 0.5s", + position="relative", + left=rx.cond( + State.sidebar_displayed, "0px", f"-{styles.sidebar_width}" + ), + ) + + return templated_page + + return decorator diff --git a/reflex/.templates/apps/default/assets/favicon.ico b/reflex/.templates/apps/blank/assets/favicon.ico similarity index 100% rename from reflex/.templates/apps/default/assets/favicon.ico rename to reflex/.templates/apps/blank/assets/favicon.ico diff --git a/reflex/.templates/apps/blank/code/__init__.py b/reflex/.templates/apps/blank/code/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/reflex/.templates/apps/default/code/default.py b/reflex/.templates/apps/blank/code/blank.py similarity index 100% rename from reflex/.templates/apps/default/code/default.py rename to reflex/.templates/apps/blank/code/blank.py diff --git a/reflex/components/typography/markdown.py b/reflex/components/typography/markdown.py index ab53480f2..993d4f48b 100644 --- a/reflex/components/typography/markdown.py +++ b/reflex/components/typography/markdown.py @@ -62,7 +62,7 @@ def get_base_component_map() -> dict[str, Callable]: "p": lambda value: Text.create(value, margin_y="1em"), "ul": lambda value: UnorderedList.create(value, margin_y="1em"), # type: ignore "ol": lambda value: OrderedList.create(value, margin_y="1em"), # type: ignore - "li": lambda value: ListItem.create(value), + "li": lambda value: ListItem.create(value, margin_y="0.5em"), "a": lambda value: Link.create(value), "code": lambda value: Code.create(value), "codeblock": lambda *_, **props: CodeBlock.create( diff --git a/reflex/constants/base.py b/reflex/constants/base.py index 736d5d2b7..8957edfb7 100644 --- a/reflex/constants/base.py +++ b/reflex/constants/base.py @@ -89,6 +89,8 @@ class Templates(SimpleNamespace): WEB_TEMPLATE = os.path.join(BASE, "web") # The jinja template directory. JINJA_TEMPLATE = os.path.join(BASE, "jinja") + # Where the code for the templates is stored. + CODE = "code" class Next(SimpleNamespace): diff --git a/reflex/page.py b/reflex/page.py index e5ad8918a..784b4ee67 100644 --- a/reflex/page.py +++ b/reflex/page.py @@ -61,3 +61,15 @@ def page( return render_fn return decorator + + +def get_decorated_pages() -> list[dict]: + """Get the decorated pages. + + Returns: + The decorated pages. + """ + return sorted( + [page_data for render_fn, page_data in DECORATED_PAGES], + key=lambda x: x["route"], + ) diff --git a/reflex/reflex.py b/reflex/reflex.py index 98bc4745f..1901ef94c 100644 --- a/reflex/reflex.py +++ b/reflex/reflex.py @@ -71,7 +71,7 @@ def init( None, metavar="APP_NAME", help="The name of the app to initialize." ), template: constants.Templates.Kind = typer.Option( - constants.Templates.Kind.DEFAULT.value, + constants.Templates.Kind.BASE.value, help="The template to initialize the app with.", ), loglevel: constants.LogLevel = typer.Option( diff --git a/reflex/testing.py b/reflex/testing.py index 8d204c3d5..814b5673e 100644 --- a/reflex/testing.py +++ b/reflex/testing.py @@ -154,7 +154,7 @@ class AppHarness: with chdir(self.app_path): reflex.reflex.init( name=self.app_name, - template=reflex.constants.Templates.Kind.DEFAULT, + template=reflex.constants.Templates.Kind.BLANK, loglevel=reflex.constants.LogLevel.INFO, ) self.app_module_path.write_text(source_code) diff --git a/reflex/utils/exec.py b/reflex/utils/exec.py index 38a959917..f43234bfb 100644 --- a/reflex/utils/exec.py +++ b/reflex/utils/exec.py @@ -34,7 +34,7 @@ def detect_package_change(json_file_path: str) -> str: """Calculates the SHA-256 hash of a JSON file and returns it as a hexadecimal string. Args: - json_file_path (str): The path to the JSON file to be hashed. + json_file_path: The path to the JSON file to be hashed. Returns: str: The SHA-256 hash of the JSON file as a hexadecimal string. diff --git a/reflex/utils/path_ops.py b/reflex/utils/path_ops.py index 939249267..ce1b88d98 100644 --- a/reflex/utils/path_ops.py +++ b/reflex/utils/path_ops.py @@ -4,6 +4,7 @@ from __future__ import annotations import json import os +import re import shutil from pathlib import Path @@ -173,3 +174,21 @@ def update_json_file(file_path: str, update_dict: dict[str, int | str]): # Write the updated json object to the file with open(fp, "w") as f: json.dump(json_object, f, ensure_ascii=False) + + +def find_replace(directory: str, find: str, replace: str): + """Recursively find and replace text in files in a directory. + + Args: + directory: The directory to search. + find: The text to find. + replace: The text to replace. + """ + for root, _dirs, files in os.walk(directory): + for file in files: + filepath = os.path.join(root, file) + with open(filepath, "r", encoding="utf-8") as f: + text = f.read() + text = re.sub(find, replace, text) + with open(filepath, "w") as f: + f.write(text) diff --git a/reflex/utils/prerequisites.py b/reflex/utils/prerequisites.py index e4f71cfc0..0e94f1624 100644 --- a/reflex/utils/prerequisites.py +++ b/reflex/utils/prerequisites.py @@ -249,17 +249,25 @@ def initialize_app_directory(app_name: str, template: constants.Templates.Kind): template: The template to use. """ console.log("Initializing the app directory.") - path_ops.cp( - os.path.join(constants.Templates.Dirs.BASE, "apps", template.value, "code"), - app_name, - ) + + # Copy the template to the current directory. + template_dir = os.path.join(constants.Templates.Dirs.BASE, "apps", template.value) + for file in os.listdir(template_dir): + # Copy the file but keep the name the same. + path_ops.cp(os.path.join(template_dir, file), file) + + # Rename the template app to the app name. + path_ops.mv(constants.Templates.Dirs.CODE, app_name) path_ops.mv( - os.path.join(app_name, template.value + ".py"), + os.path.join(app_name, template.value + constants.Ext.PY), os.path.join(app_name, app_name + constants.Ext.PY), ) - path_ops.cp( - os.path.join(constants.Templates.Dirs.BASE, "apps", template.value, "assets"), - constants.Dirs.APP_ASSETS, + + # Fix up the imports. + path_ops.find_replace( + app_name, + f"from {constants.Templates.Dirs.CODE}", + f"from {app_name}", )