diff --git a/docs/ai_builder/apis.md b/docs/ai_builder/apis.md deleted file mode 100644 index a859a3d25..000000000 --- a/docs/ai_builder/apis.md +++ /dev/null @@ -1,41 +0,0 @@ -# API Integration - -Not every service has a first-class integration — but your app can connect to **any external API** directly. By using standard HTTP requests, you can integrate with virtually any platform, fetch or send data, and trigger workflows without needing a prebuilt connector. - -This gives you full flexibility to work with modern REST, GraphQL, or other web APIs. - -## What You Can Do - -With custom API calls, your app can: -- Connect to **any REST or GraphQL API** on the web. -- **Send and receive data** from external services. -- Trigger actions like creating records, sending messages, or fetching analytics. -- Build **custom automations** and workflows around APIs. -- Chain API calls with other integrations or AI actions for powerful flows. - -## Step 1: Get API Access - -1. Identify the service you want to connect to. -2. Check its developer documentation for API access requirements. -3. Obtain the necessary credentials (e.g., **API key**, **token**, or **OAuth**). -4. Store credentials securely using environment variables — **never** hardcode secrets. - - *Example:* - - **API Key:** `sk-xxxxxxxxxxxxxxxx` - - **Base URL:** `https://api.example.com/v1/` - -## Step 2: Hook up with your App - -1. In the AI Builder navigate to the `secrets` tab and add your API credentials as secrets. -2. Then prompt the AI to use these secrets to do what you want and it will install the necessary libraries and set up the API calls for you. - -## Step 3: Notes - -* **Keep secrets safe:** Use environment variables or secret storage for API keys. -* **Check rate limits:** Many APIs have request limits — build accordingly. -* **Combine with AI or other integrations:** For example, fetch data via API and summarize it using an LLM. - - -With API integrations, you can connect your app to **almost any modern platform or service**, giving you unlimited extensibility beyond native integrations. - - diff --git a/docs/ai_builder/app_lifecycle/copy_app.md b/docs/ai_builder/app_lifecycle/copy_app.md deleted file mode 100644 index 7c794b030..000000000 --- a/docs/ai_builder/app_lifecycle/copy_app.md +++ /dev/null @@ -1,56 +0,0 @@ -# Copy App - -The **Copy** feature lets you duplicate an existing app inside Reflex Build. -This is useful when you want to experiment with changes without affecting the original project, or when you want to use an app as a starting point for a new idea. - - -```python exec -import reflex as rx -from pcweb.constants import REFLEX_ASSETS_CDN -from pcweb.components.image_zoom import image_zoom - -def render_image(): - return rx.el.div( - image_zoom( - rx.image( - src=f"{REFLEX_ASSETS_CDN}ai_builder/app_lifecycle/copy_light.avif", - class_name="p-2 rounded-md h-auto", - border=f"0.81px solid {rx.color('slate', 5)}", - ), - class_name="rounded-md overflow-hidden", - ), - class_name="w-full flex flex-col rounded-md cursor-pointer", - ) -``` - -```python eval - -rx.el.div(render_image()) - -``` - -## How to Copy an App - -1. In the Reflex Build workspace, click on the arrow down icon next to the deploy button and click on the **Copy** button. You can also do this in the Settings tab. -2. Reflex Build will create a new app in your workspace with the same: - - Code files and components - - State and configuration - - Dependencies - -The copied app will appear as a separate project, independent from the original. - - -## Common Use Cases - -- **Experiment Safely** - Try out new components, layouts, or integrations without risking your working app. - -- **Create Variations** - Use the original app as a base to quickly spin up a different version (e.g., a light and dark theme version). - -- **Template Reuse** - Turn an app into a personal template and copy it each time you start a new project. - -## Best Practices - -- Rename your copied app immediately so it’s easy to distinguish from the original. diff --git a/docs/ai_builder/app_lifecycle/deploy_app.md b/docs/ai_builder/app_lifecycle/deploy_app.md deleted file mode 100644 index f168a8a77..000000000 --- a/docs/ai_builder/app_lifecycle/deploy_app.md +++ /dev/null @@ -1,45 +0,0 @@ -# Deploy App - -```python exec -import reflex as rx -``` - -It is easy to deploy your app into production from Reflex Build to Reflex Cloud. - -Simply click the `Deploy` button in the top right corner of Reflex Build, as shown below: - - - -```python exec -import reflex as rx -from pcweb.constants import REFLEX_ASSETS_CDN -from pcweb.components.image_zoom import image_zoom - -def render_image(): - return rx.el.div( - image_zoom( - rx.image( - src=f"{REFLEX_ASSETS_CDN}ai_builder/app_lifecycle/deploy_light.avif", - class_name="p-2 rounded-md h-auto", - border=f"0.81px solid {rx.color('slate', 5)}", - ), - class_name="rounded-md overflow-hidden", - ), - class_name="w-full flex flex-col rounded-md cursor-pointer", - ) -``` - -```python eval - -rx.el.div(render_image()) - -``` - -When deploying you can set the following options: -- **App Name**: The name of your app -- **Hostname**: Set your url by setting your hostname, i.e. if you set `myapp` as your hostname, your app will be available at `myapp.reflex.run` -- **Region**: The regions where your app will be deployed -- **VM Size**: The size of the VM where your app will be deployed -- **Secrets**: The environment variables that will be set for your app, you can load the variables currently being used by your app by clicking the `Load from settings` button - -Note: Hostname customization, region selection, and VM sizing are only available on paid plans. diff --git a/docs/ai_builder/app_lifecycle/download_app.md b/docs/ai_builder/app_lifecycle/download_app.md deleted file mode 100644 index c011c2bf9..000000000 --- a/docs/ai_builder/app_lifecycle/download_app.md +++ /dev/null @@ -1,39 +0,0 @@ -# Download App - -You can download your Reflex Build project if you want to work on it locally or self-host it outside the AI Builder. - -**Tip:** The recommended workflow is to use the GitHub integration, which keeps your code version-controlled and in sync. Downloading is useful if GitHub integration isn’t available or you just want a one-time export. - - -```python exec -import reflex as rx -from pcweb.constants import REFLEX_ASSETS_CDN -from pcweb.components.image_zoom import image_zoom - -def render_image(): - return rx.el.div( - image_zoom( - rx.image( - src=f"{REFLEX_ASSETS_CDN}ai_builder/app_lifecycle/download_light.avif", - class_name="p-2 rounded-md h-auto", - border=f"0.81px solid {rx.color('slate', 5)}", - ), - class_name="rounded-md overflow-hidden", - ), - class_name="w-full flex flex-col rounded-md cursor-pointer", - ) -``` - -```python eval - -rx.el.div(render_image()) - -``` - -## How to Download - -1. In the AI Builder workspace, click on the arrow down icon next to the deploy button and click on the **Download** button. You can also do this in the Settings tab. -2. A `.zip` file will be generated containing your entire Reflex app, including: - - Source code (`.py` files, components, state, etc.) - - `requirements.txt` with dependencies - - Config files (`rxconfig.py`, `.env`, etc.) diff --git a/docs/ai_builder/app_lifecycle/fork_app.md b/docs/ai_builder/app_lifecycle/fork_app.md deleted file mode 100644 index 2b049a39c..000000000 --- a/docs/ai_builder/app_lifecycle/fork_app.md +++ /dev/null @@ -1,48 +0,0 @@ -# Fork App - -The **Fork App** feature lets you take an existing app and create your own version of it. This is perfect for **experimenting, customizing, or building on top of someone else’s work** without affecting the original app. - -```python exec -import reflex as rx -from pcweb.constants import REFLEX_ASSETS_CDN -from pcweb.components.image_zoom import image_zoom -``` - -```python eval -rx.el.div( - image_zoom( - rx.image( - src=f"{REFLEX_ASSETS_CDN}ai_builder/overview/fork_template_light.avif", - class_name="p-2 rounded-md h-auto", - border=f"0.81px solid {rx.color('slate', 5)}", - ), - class_name="rounded-md overflow-hidden", - ), - class_name="w-full flex flex-col rounded-md cursor-pointer", -) -``` - - -## How to Fork an App - -1. Browse or open an app you’d like to use as a starting point. -2. Click the **Fork** button in the app’s top right corner. -3. The AI Builder will create a **copy of the app** in your workspace. -4. You can now **edit, customize, and expand** your forked app independently of the original. - -## What Happens When You Fork - -- You get a **full copy** of the original app, including all pages, components, and configurations. -- The forked app is **completely separate**, so changes you make do not affect the original. -- You can **rename, deploy, or share** your forked app like any other app in your workspace. - -## Common Use Cases - -- **Start From an Example** - Use a sample or shared app as a foundation to save time and learn best practices. - -- **Experiment Safely** - Try new ideas or features without risking changes to the original app. - -- **Collaborate and Customize** - Fork a teammate’s app to tailor it to your needs while keeping the original intact. diff --git a/docs/ai_builder/app_lifecycle/general.md b/docs/ai_builder/app_lifecycle/general.md deleted file mode 100644 index 757773ca2..000000000 --- a/docs/ai_builder/app_lifecycle/general.md +++ /dev/null @@ -1,55 +0,0 @@ -# General App Settings - -The **General App Settings** section lets you manage key aspects of your app, including its name, ID, and deletion. This is your central place to view and update your app’s core information. - - -```python exec -import reflex as rx -from pcweb.constants import REFLEX_ASSETS_CDN -from pcweb.components.image_zoom import image_zoom - -def render_image(): - return rx.el.div( - image_zoom( - rx.image( - src=f"{REFLEX_ASSETS_CDN}ai_builder/app_lifecycle/general_light.avif", - class_name="p-2 rounded-md h-auto", - border=f"0.81px solid {rx.color('slate', 5)}", - ), - class_name="rounded-md overflow-hidden", - ), - class_name="w-full flex flex-col rounded-md cursor-pointer", - ) -``` - -```python eval - -rx.el.div(render_image()) - -``` - - -## How to Access Settings - -1. In the AI Builder workspace, on the top bar click the more 3 dots icon and then click the **Settings** tab. -2. This will open the **Settings** tab to see your app’s main settings. - -## What You Can Do - -- **Change App Name** - Update the name of your app to reflect its purpose or version. - -- **Change App Visibility** - Update the visibility of your app to public or private. - -- **View App ID** - Find the unique identifier for your app, which can be used for integrations or support. - -- **Fork App** - Fork your app to create a copy of it. - --- **Download App** - Download your app to your local machine. - -- **Delete App** - Permanently remove an app you no longer need. **Warning:** This action cannot be undone. diff --git a/docs/ai_builder/app_lifecycle/share_app.md b/docs/ai_builder/app_lifecycle/share_app.md deleted file mode 100644 index 7bee644f7..000000000 --- a/docs/ai_builder/app_lifecycle/share_app.md +++ /dev/null @@ -1,54 +0,0 @@ -# Share App - -The **Share** feature makes it easy to show your app to others without deploying it. -When you share, Reflex Build generates a unique link that points to the current version of your project in the builder. - -```python exec -import reflex as rx -from pcweb.constants import REFLEX_ASSETS_CDN -from pcweb.components.image_zoom import image_zoom - -def render_image(): - return rx.el.div( - image_zoom( - rx.image( - src=f"{REFLEX_ASSETS_CDN}ai_builder/app_lifecycle/share_light.avif", - class_name="p-2 rounded-md h-auto", - border=f"0.81px solid {rx.color('slate', 5)}", - ), - class_name="rounded-md overflow-hidden", - ), - class_name="w-full flex flex-col rounded-md cursor-pointer", - ) -``` - -```python eval - -rx.el.div(render_image()) - -``` - -## How to Share - -1. In the AI Builder workspace, click on the arrow down icon next to the deploy button and click on the **Share** button. -2. A popup will appear with a **shareable link**. -3. Copy the link and send it to teammates, collaborators, or stakeholders. - - -## What Others See - -- The link opens a **read-only view** of your app generation. -- Recipients can see the app preview but cannot make edits. -- This makes it safe to share work-in-progress versions for quick feedback. - - -## Common Use Cases - -- **Get Feedback Quickly** - Share a work-in-progress version with your team before deploying. - -- **Demo Features** - Send a link to showcase a new component, layout, or integration. - -- **Collaboration** - Share context with another developer before handing off to GitHub or download. diff --git a/docs/ai_builder/features/automated_testing.md b/docs/ai_builder/features/automated_testing.md deleted file mode 100644 index 3ebf60e2e..000000000 --- a/docs/ai_builder/features/automated_testing.md +++ /dev/null @@ -1,60 +0,0 @@ -# AI Testing Feature - -## Overview - -The Testing feature allows you to automatically test your generated applications for common issues and functionality problems. The AI will analyze your app and identify potential bugs, broken links, navigation issues, and other problems. - - -```python exec -import reflex as rx -from pcweb.constants import REFLEX_ASSETS_CDN -from pcweb.components.image_zoom import image_zoom -``` - -```python eval -rx.el.div( - image_zoom( - rx.image( - src=rx.color_mode_cond( - f"{REFLEX_ASSETS_CDN}ai_builder/features/test_light.webp", - f"{REFLEX_ASSETS_CDN}ai_builder/features/test_dark.webp", - ), - class_name="p-2 rounded-md h-auto", - border=f"0.81px solid {rx.color('slate', 5)}", - ), - class_name="rounded-md overflow-hidden", - ), - class_name="w-full flex flex-col rounded-md cursor-pointer", -) -``` - -## How to Use - -1. **Start Testing**: Type "test this app" or similar command to activate testing mode -2. **AI Analysis**: The AI will automatically switch to testing mode and begin analyzing your application -3. **Review Results**: The preview tab switches to "Testing" mode to show the testing process and results - -## What Gets Tested - -The AI automatically checks for: - -- **Broken Navigation**: Links that don't work or lead to missing pages -- **Non-functional Buttons**: Buttons that don't respond or trigger errors -- **Broken Links**: External or internal links that return errors -- **UI/UX Issues**: Interface elements that don't function as expected -- **Data Flow Problems**: Issues with forms, inputs, and data handling -- **Layout Issues**: Visual or structural problems with the interface - -## Testing Interface - -When testing is active: -- The preview tab changes to "Testing" mode -- You can see the AI interact with your application in real-time -- Issues and results are reported as they're discovered -- The testing process is visual and interactive - -## Benefits - -- **Quality Assurance**: Catch issues before deployment -- **Time Saving**: Automated testing is faster than manual checking -- **Comprehensive Coverage**: Tests multiple aspects of your application diff --git a/docs/ai_builder/features/connect_to_github.md b/docs/ai_builder/features/connect_to_github.md deleted file mode 100644 index f97c6a7e2..000000000 --- a/docs/ai_builder/features/connect_to_github.md +++ /dev/null @@ -1,50 +0,0 @@ ---- -tags: DevTools -description: Integrate with GitHub to automate workflows and interact with your code repositories. ---- - -# Connecting to Github - -```python exec -import reflex as rx -from pcweb.constants import REFLEX_ASSETS_CDN -from pcweb.components.image_zoom import image_zoom -``` - -The Github integration is important to make sure that you don't lose your progress. It also allows you to revert to previous versions of your app. - - -```python eval -rx.el.div( - image_zoom( - rx.image( - src=f"{REFLEX_ASSETS_CDN}ai_builder/connecting_to_github.avif", - class_name="p-2 rounded-md h-auto", - border=f"0.81px solid {rx.color('slate', 5)}", - ), - ), -) -``` - -The GitHub integration allows you to: - -- Save your app progress -- Work on your code locally and push your local changes back to Reflex.Build - - -## Github Commit History - -The commit history is a great way to see the changes that you have made to your app. You can also revert to previous versions of your app from here. - -```python eval -rx.el.div( - image_zoom( - rx.image( - src=f"{REFLEX_ASSETS_CDN}ai_builder/github_commit_history.avif", - class_name="p-2 rounded-md h-auto", - border=f"0.81px solid {rx.color('slate', 5)}", - ), - ), -) -``` - diff --git a/docs/ai_builder/features/customization.md b/docs/ai_builder/features/customization.md deleted file mode 100644 index 17a93efd5..000000000 --- a/docs/ai_builder/features/customization.md +++ /dev/null @@ -1,72 +0,0 @@ -# App Style Customization - -## Overview - -The App Style feature allows you to customize the visual appearance of your AI-generated applications. You can choose from predefined design themes or create custom styling to match your brand and preferences. - -## How to Use - -1. **Access the Feature**: Click on the App Style option to open the customization panel -2. **Choose Your Approach**: - - **Custom**: Manually configure individual design elements - - **Themes**: Select from professionally designed templates - -## Custom Styling Options - -```python exec -import reflex as rx -from pcweb.constants import REFLEX_ASSETS_CDN -from pcweb.components.image_zoom import image_zoom -``` - -```python eval -rx.el.div( - image_zoom( - rx.image( - src=rx.color_mode_cond( - f"{REFLEX_ASSETS_CDN}ai_builder/features/style_light.webp", - f"{REFLEX_ASSETS_CDN}ai_builder/features/style_dark.webp", - ), - class_name="p-2 rounded-md h-auto", - border=f"0.81px solid {rx.color('slate', 5)}", - ), - class_name="rounded-md overflow-hidden", - ), - class_name="w-full flex flex-col rounded-md cursor-pointer", -) -``` - -When using Custom mode, you can adjust: - -- **Primary Color**: Choose your main brand color from 20+ preset options -- **Secondary Color**: Select a complementary color for accents -- **Typography**: Pick a font family for your app -- **Border Radius**: Control how rounded corners appear -- **Shadows**: Add depth with shadow effects -- **Spacing**: Adjust the spacing between elements - -## Themes - -These options are predefined popular themes you can choose for your app. Below are some of the available themes you can choose from - -```python eval -rx.el.div( - image_zoom( - rx.image( - src=rx.color_mode_cond( - f"{REFLEX_ASSETS_CDN}ai_builder/features/theme_light.webp", - f"{REFLEX_ASSETS_CDN}ai_builder/features/theme_dark.webp", - ), - class_name="p-2 rounded-md h-auto", - border=f"0.81px solid {rx.color('slate', 5)}", - ), - class_name="rounded-md overflow-hidden", - ), - class_name="w-full flex flex-col rounded-md cursor-pointer", -) -``` - -- **Minimal Design**: Clean, geometric design with no shadows or gradients -- **Modern UI**: Sleek, contemporary interface optimized for performance -- **Carbon Design**: Enterprise-grade design following IBM's Carbon system -- **Material Design**: Google's design language with elevation and semantic colors diff --git a/docs/ai_builder/features/editor_modes.md b/docs/ai_builder/features/editor_modes.md deleted file mode 100644 index a5bd7d0f9..000000000 --- a/docs/ai_builder/features/editor_modes.md +++ /dev/null @@ -1,90 +0,0 @@ -# Editor Modes - -The AI Builder includes a powerful dual-mode editor that lets you view and edit your application code while tracking changes made by the AI. You can seamlessly switch between **Editor Mode** for manual code editing and **Diff Mode** for reviewing AI-generated changes. - - -```python exec -import reflex as rx -from pcweb.constants import REFLEX_ASSETS_CDN -from pcweb.components.image_zoom import image_zoom - -def render_image(): - return rx.el.div( - image_zoom( - rx.image( - src=f"{REFLEX_ASSETS_CDN}ai_builder/features/diff_light.avif", - class_name="p-2 rounded-md h-auto", - border=f"0.81px solid {rx.color('slate', 5)}", - ), - class_name="rounded-md overflow-hidden", - ), - class_name="w-full flex flex-col rounded-md cursor-pointer", - ) -``` - -```python eval - -rx.el.div(render_image()) - -``` -## Modes: Editor vs Diff - -### Editor Mode -The standard code editor where you can: -- **Write and modify code** directly in the interface -- **Navigate through files** using the file tree -- **Make manual changes** to your application -- **Save your modifications** which persist across sessions - -### Diff Mode -A specialized view that highlights changes from the last AI prompt: -- **Green highlights** show code additions made by the AI -- **Red highlights** show code deletions made by the AI -- **Side-by-side comparison** of what changed -- **Line-by-line tracking** of modifications - -## Switching Between Modes - -### Toggle Controls -Located in the editor toolbar, you'll find: -- **Editor** button - Switch to normal editing mode -- **Diff** button - Switch to change tracking mode - -### When to Use Each Mode -- **Use Editor Mode when:** - - Making manual code changes - - Writing new functionality - - Debugging or fixing issues - - General code development - -- **Use Diff Mode when:** - - Reviewing what the AI changed after a prompt - - Understanding modifications before accepting them - - Tracking the impact of AI suggestions - - Learning from AI-generated code patterns - -## Understanding Diff Visualizations - -### Code Highlighting -**Additions (Green):** -- New code lines added by the AI -- New functions, components, or logic -- Enhanced features and improvements - -**Deletions (Red):** -- Code removed by the AI -- Replaced or refactored sections -- Deprecated functionality - -### File Tree Indicators -The file tree shows change statistics for each modified file: - -**Change Indicators:** -- **`+5`** - 5 lines added to this file -- **`-3`** - 3 lines removed from this file -- **`+12 -8`** - 12 lines added, 8 lines removed -- **No indicator** - File unchanged - -**Visual Cues:** -- **Green `+` symbol** indicates files with additions -- **Red `-` symbol** indicates files with deletions diff --git a/docs/ai_builder/features/file_tree.md b/docs/ai_builder/features/file_tree.md deleted file mode 100644 index e09f76cda..000000000 --- a/docs/ai_builder/features/file_tree.md +++ /dev/null @@ -1,29 +0,0 @@ -# File Tree - -The **File Tree** in Reflex Build lets you **view, organize, and manage all files and folders** in your project directly from the browser. It’s your central hub for navigating your app’s structure. - -## Key Features - -### Creating Folders -- Click the **New Folder** button to create a folder inside the currently selected directory. -- Enter a name and press **Enter** to confirm. -- Folders can be nested to organize your project hierarchically. - -### Creating Files -- Click the **New File** button to create a file inside the current folder. -- Enter a file name and extension (e.g., `main.py`) and press **Enter**. -- Files are immediately visible in the tree and ready for editing in the code editor. - -### Renaming Files or Folders -- Right-click on a file or folder and select **Rename**. -- Type the new name and press **Enter** to confirm. -- Renaming automatically updates references in your project where applicable. - -### Deleting Files or Folders -- Right-click on a file or folder and select **Delete**. -- Deleted items are permanently removed, so be careful when deleting important files. - -### Drag-and-Drop from System -- Drag files or folders from your computer directly into the File Tree. -- The editor will automatically import them into the selected folder. -- This works for individual files or entire folder structures, making adding assets or scripts quick and easy. diff --git a/docs/ai_builder/features/ide.md b/docs/ai_builder/features/ide.md deleted file mode 100644 index 46a8fc63f..000000000 --- a/docs/ai_builder/features/ide.md +++ /dev/null @@ -1,33 +0,0 @@ -# Reflex Build IDE - -Reflex Build includes a **powerful, in-browser IDE** built on **Monaco Editor**, designed to make coding fast, efficient, and enjoyable—all without leaving your browser. - -
- -
- - -## Features - -### Real-Time Editing -Edit your code and see changes reflected immediately in your project. No manual saves or rebuilds—stay focused and iterate faster. - -### Syntax Highlighting & Error Detection -Write code confidently with **syntax highlighting**, **inline error alerts**, and **linting**. Catch issues as you type, reducing bugs and speeding up development. - -### Code Snippets & Autocomplete -Speed up development with **autocomplete** for functions, variables, and imports, as well as reusable **code snippets** for common patterns. - -### Integrated Preview -Quickly preview your changes directly in the editor without switching contexts, ensuring your app behaves as expected as you code. - -### Built-In Terminal for Debugging -Use the integrated terminal to run commands, debug issues, and inspect logs—all within the IDE. No need to switch tools or tabs. diff --git a/docs/ai_builder/features/image_as_prompt.md b/docs/ai_builder/features/image_as_prompt.md deleted file mode 100644 index 30141dc09..000000000 --- a/docs/ai_builder/features/image_as_prompt.md +++ /dev/null @@ -1,32 +0,0 @@ -# Use Images as a prompt - -```python exec -import reflex as rx -from pcweb.constants import REFLEX_ASSETS_CDN -from pcweb.components.image_zoom import image_zoom -``` - -Uploading an image (screenshot) of a website (web) app of what you are looking to build gives the AI really good context. - -*This is the recommended way to start an app generation.* - - -Below is an image showing how to upload an image to the AI Builder, you can click on the "Attach" button to upload an image, drag and drop an image, or paste an image from the clipboard: - -```python eval -rx.el.div( - image_zoom( - rx.image( - src=f"{REFLEX_ASSETS_CDN}ai_builder/image_upload.avif", - class_name="p-2 rounded-md h-auto", - border=f"0.81px solid {rx.color('slate', 5)}", - ), - class_name="rounded-md overflow-hidden", - ), - class_name="w-full flex flex-col rounded-md cursor-pointer", -) -``` - -The advised prompt to use is: - -`Build an app from a reference image` \ No newline at end of file diff --git a/docs/ai_builder/features/installing_external_packages.md b/docs/ai_builder/features/installing_external_packages.md deleted file mode 100644 index 8950fb6e4..000000000 --- a/docs/ai_builder/features/installing_external_packages.md +++ /dev/null @@ -1,51 +0,0 @@ -# Installing External Packages - -```python exec -import reflex as rx -from pcweb.constants import REFLEX_ASSETS_CDN -from pcweb.components.image_zoom import image_zoom -``` - -Reflex Build allows you to install external python packages to use in your app. This is useful if you want to use a package that is not included in the default Reflex Build environment. Examples might include `openai`, `langsmith`, `requests`, etc. - -There are two ways to install external packages: - -1. **Through the Chat Interface**: You can ask the AI to install a package for you. -2. **Add to the `requirements.txt` file**: You can add the package to the `requirements.txt` file and then save the app. This will install the package in your app's environment. - -## Installing through the Chat Interface - -Enter the name of the package you want to install in the chat interface. The AI will then install the package for you. - - -```python eval -rx.el.div( - image_zoom( - rx.image( - src=f"{REFLEX_ASSETS_CDN}ai_builder/external_packages_input.avif", - class_name="p-2 rounded-md h-auto", - border=f"0.81px solid {rx.color('slate', 5)}", - ), - class_name="rounded-md overflow-hidden", - ), - class_name="w-full flex flex-col rounded-md cursor-pointer", -) -``` - -## Installing through the requirements.txt file - -Add the package to the `requirements.txt` file and then save the app. This will install the package in your app's environment and recompile your app. - -```python eval -rx.el.div( - image_zoom( - rx.image( - src=f"{REFLEX_ASSETS_CDN}ai_builder/external_packages_requirements.avif", - class_name="p-2 rounded-md h-auto", - border=f"0.81px solid {rx.color('slate', 5)}", - ), - class_name="rounded-md overflow-hidden", - ), - class_name="w-full flex flex-col rounded-md cursor-pointer", -) -``` \ No newline at end of file diff --git a/docs/ai_builder/features/integration_shortcut.md b/docs/ai_builder/features/integration_shortcut.md deleted file mode 100644 index 30a6915c8..000000000 --- a/docs/ai_builder/features/integration_shortcut.md +++ /dev/null @@ -1,52 +0,0 @@ -# Integrations Shortcut - -Reflex Build supports powerful integrations like databases, OpenAI, and Databricks, allowing you to connect external services to your app without complex setup. These integrations help you add advanced functionality—like AI-powered features, data analytics, or persistent storage—while speeding up development. - -The **Add Integrations** button makes it easy to connect external services to your app while chatting with the AI Builder. A panel with a list of available integrations will appear, you can quickly add integrations that your app can reference and connect to. - -Once in your app, you can access your integrations by clicking the flow or cog icon in the bottom left inside the chat area. - -```python exec -import reflex as rx -from pcweb.constants import REFLEX_ASSETS_CDN -from pcweb.components.image_zoom import image_zoom -``` - -```python eval -rx.el.div( - image_zoom( - rx.image( - src=rx.color_mode_cond( - f"{REFLEX_ASSETS_CDN}ai_builder/features/shortcut_light.webp", - f"{REFLEX_ASSETS_CDN}ai_builder/features/shortcut_dark.webp", - ), - class_name="p-2 rounded-md h-auto", - border=f"0.81px solid {rx.color('slate', 5)}", - ), - class_name="rounded-md overflow-hidden", - ), - class_name="w-full flex flex-col rounded-md cursor-pointer", -) -``` - -## How to Use - -1. In the AI Builder home, click the **Add Integrations** button. And if you're already in an app, click the flow or cog icon in the bottom left inside the chat area. -2. A list of available integrations will appear (e.g. Database, Databricks, OpenAI, etc.). -3. Click the Add button next to an integration to select it. -4. The integration will be added to your app and becomes available for connection. Then you can fill the required fields for the integration. -5. The AI Builder now knows your app has access to this integration and can generate code that uses it. - -## What It Does - -- **Quick Access** – Easily browse and select from available integrations. -- **Automatic Connection** – Integrations are added to your app and become available for the AI to use in generated code. -- **No Manual Setup** – Skip complex configuration—the AI Builder handles the integration setup for you. - -## Common Use Cases - -- **Database Queries** - Show me the top 10 users ordered by signup date from my database. - -- **AI Features** - Create a chat application that uses OpenAI to generate responses. diff --git a/docs/ai_builder/features/knowledge.md b/docs/ai_builder/features/knowledge.md deleted file mode 100644 index 8bddca4ad..000000000 --- a/docs/ai_builder/features/knowledge.md +++ /dev/null @@ -1,51 +0,0 @@ -# Knowledge - -The **Knowledge** feature lets you add context or rules that the AI Builder can reference when generating apps. This ensures your apps follow your guidelines, standards, or specific business logic. - -```python exec -import reflex as rx -from pcweb.constants import REFLEX_ASSETS_CDN -from pcweb.components.image_zoom import image_zoom - -def render_image(): - return rx.el.div( - image_zoom( - rx.image( - src=f"{REFLEX_ASSETS_CDN}ai_builder/features/knowledge_light.avif", - class_name="p-2 rounded-md h-auto", - border=f"0.81px solid {rx.color('slate', 5)}", - ), - class_name="rounded-md overflow-hidden", - ), - class_name="w-full flex flex-col rounded-md cursor-pointer", - ) -``` - -```python eval - -rx.el.div(render_image()) - -``` - -## How to Add Knowledge - -1. In the AI Builder top bar, click the more 3 dots icon and then click the **`Knowledge`** tab. -2. Enter your rule, guideline, or context description. -3. Save the entry. The AI Builder will automatically use it when generating apps. - -## How It Works - -- The AI Builder references your knowledge entries as rules or guidelines. -- Rules can define naming conventions, component usage, layout preferences, or other custom logic. -- Multiple rules can be added to cover different aspects of app generation. - -## Common Use Cases - -- **Maintain Consistency** - Ensure all generated apps follow your company’s design or naming standards. - -- **Enforce Business Logic** - Guide the AI Builder to follow specific workflows, validations, or feature requirements. - -- **Quickly Adapt AI Behavior** - Add or update rules to influence new app generations without manual edits. diff --git a/docs/ai_builder/features/restore_checkpoint.md b/docs/ai_builder/features/restore_checkpoint.md deleted file mode 100644 index 6eff95c8b..000000000 --- a/docs/ai_builder/features/restore_checkpoint.md +++ /dev/null @@ -1,53 +0,0 @@ -# Restore Checkpoint - -The **Restore Checkpoint** feature allows you to roll back your app to any previous state during your AI Builder conversation. This is useful when you want to undo recent changes and return to an earlier version of your app. - -```python exec -import reflex as rx -from pcweb.constants import REFLEX_ASSETS_CDN -from pcweb.components.image_zoom import image_zoom -``` - -```python eval -rx.el.div( - image_zoom( - rx.image( - src=f"{REFLEX_ASSETS_CDN}ai_builder/features/restore_light.avif", - class_name="p-2 rounded-md h-auto", - border=f"0.81px solid {rx.color('slate', 5)}", - ), - class_name="rounded-md overflow-hidden", - ), - class_name="w-full flex flex-col rounded-md cursor-pointer", -) -``` - -## How It Works - -Every time the AI agent makes changes to your app, a checkpoint is automatically created. You can restore to any of these checkpoints at any time, effectively undoing all changes made after that point. - -## Using Restore Checkpoint - -1. **Locate the Restore Icon**: At the end of each AI agent message that made changes to your app, you'll see a circular arrow icon (↻). - -2. **Click to Restore**: Click the circular arrow icon next to the message you want to restore to. - -3. **Confirm the Action**: The app will restore to the exact state it was in after that specific message was processed. - -4. **Continue Building**: After restoring, you can continue the conversation and make new changes from that point. - -## When to Use Restore Checkpoint - -- **Undo Unwanted Changes**: When the AI made changes you don't like -- **Try Different Approaches**: Restore and ask the AI to implement a feature differently -- **Fix Broken Functionality**: Roll back when new changes break existing features -- **Experiment Safely**: Test different solutions knowing you can always restore to a checkpoint - -## Important Notes - -- Restoring will **permanently delete** all changes made after the selected message -- You cannot undo a restore operation - choose your restore point carefully -- The conversation history remains intact, but code changes after the restore point are lost -- Restore checkpoint only affects your current building session - -> **Tip:** Before making major changes, note which message represents your last stable checkpoint so you can easily restore if needed. diff --git a/docs/ai_builder/features/secrets.md b/docs/ai_builder/features/secrets.md deleted file mode 100644 index 8f807b2c8..000000000 --- a/docs/ai_builder/features/secrets.md +++ /dev/null @@ -1,71 +0,0 @@ -# Secrets - -The **Secrets** feature allows you to securely store environment-specific values that your app can use, such as API keys, tokens, or other sensitive information. - - -```python exec -import reflex as rx -from pcweb.constants import REFLEX_ASSETS_CDN -from pcweb.components.image_zoom import image_zoom - -def render_image(): - return rx.el.div( - image_zoom( - rx.image( - src=f"{REFLEX_ASSETS_CDN}ai_builder/features/secrets_light.avif", - class_name="p-2 rounded-md h-auto", - border=f"0.81px solid {rx.color('slate', 5)}", - ), - class_name="rounded-md overflow-hidden", - ), - class_name="w-full flex flex-col rounded-md cursor-pointer", - ) -``` - -```python eval - -rx.el.div(render_image()) - -``` - -## Adding Secrets - -### 1. Add Individually -- **Description:** Set a single secret by providing a key and value. -- **Example:** - - Key: `OPENAI_API_KEY` - - Value: `sk-xxxxxx` -- **Behavior:** The secret is encrypted and accessible to your app at runtime. - -### 2. Add in Bulk (Raw Editor) -- **Description:** Upload multiple secrets at once using a simple `VAR=VALUE` format. -- **Example:** - -```text -DATABASE_URL=postgresql://user:pass@host:5432/db -STRIPE_SECRET_KEY=sk_test_xxxxx -OPENAI_API_KEY=sk-xxxxxx -``` - -```python exec -import reflex as rx -from pcweb.constants import REFLEX_ASSETS_CDN -from pcweb.components.image_zoom import image_zoom -``` - -```python eval -rx.el.div( - image_zoom( - rx.image( - src=f"{REFLEX_ASSETS_CDN}ai_builder/features/secret_bulk_light.avif", - class_name="p-2 rounded-md h-auto", - border=f"0.81px solid {rx.color('slate', 5)}", - ), - class_name="rounded-md overflow-hidden", - ), - class_name="w-full flex flex-col rounded-md cursor-pointer", -) -``` - - -- **Behavior:** Each secret is securely stored and immediately available in the app environment. diff --git a/docs/ai_builder/figma.md b/docs/ai_builder/figma.md deleted file mode 100644 index 36f159736..000000000 --- a/docs/ai_builder/figma.md +++ /dev/null @@ -1 +0,0 @@ -Integration Coming Soon! \ No newline at end of file diff --git a/docs/ai_builder/files.md b/docs/ai_builder/files.md deleted file mode 100644 index 232819be3..000000000 --- a/docs/ai_builder/files.md +++ /dev/null @@ -1,37 +0,0 @@ -```python exec -from pcweb.pages.docs import ai_builder -``` -# Files - -To upload a file to the AI Builder click the `📎 Attach` button and select the file you want to upload from your computer. You can also drag and drop files directly into the chat window. - -This section does not cover uploading images. Check out [Images]({ai_builder.images.path}) to learn more about uploading images. - -```md alert -## Supported File Types -The AI Builder currently supports the following file types for upload and processing: -1. `.pdf` -2. `.doc` -3. `.docx` -4. `.xls` -5. `.xlsx` -6. `.ppt` -7. `.pptx` -8. `.odt` -9. `.ods` -10. `.odp` -11. `.rtf` -12. `.csv` -13. `.txt` -14. `.md` -15. `.markdown` -16. `.json` -17. `.xml` -18. `.yaml` -19. `.yml` -20. `.tsv` -``` - -The files you upload will automatically be added to the `assets/` folder of your app, and the AI Builder will be able to read and process their contents as part of your prompts. - -The maximum number of files you can upload at a time is `5`. The maximum file size for uploads is `5MB`. If you need to work with larger files, consider breaking them into smaller chunks or using external storage solutions and linking to them via APIs. \ No newline at end of file diff --git a/docs/ai_builder/images.md b/docs/ai_builder/images.md deleted file mode 100644 index 0400762cd..000000000 --- a/docs/ai_builder/images.md +++ /dev/null @@ -1,48 +0,0 @@ -# Images - -## Upload an image as context for AI Builder - -To upload an image to the AI Builder that will be used as context for the AI builder to build an app that is similar to the image click the `📎 Attach` button and select the file you want to upload from your computer. You can also drag and drop files directly into the chat window. - -```md alert -## Supported Image Types -The AI Builder currently supports the following image types for upload and processing: -1. `.png` -2. `.jpg` -3. `.jpeg` -4. `.webp` -5. `.gif` -6. `.svg` -7. `.bmp` -8. `.tiff` -9. `.tif` -10. `.ico` -``` - - -## Upload an image to be used within the app - -If you want to upload an image to be used within the app, such as a company logo, then you can manually upload it to the `assets/` folder within the `code` tab. Drag and drop the image into the `assets/` folder. - -```python exec -import reflex as rx -from pcweb.constants import REFLEX_ASSETS_CDN -from pcweb.components.image_zoom import image_zoom -``` - -```python eval -rx.el.div( - image_zoom( - rx.image( - src=f"{REFLEX_ASSETS_CDN}ai_builder/add_images_to_assets.avif", - class_name="p-2 rounded-md h-auto", - border=f"0.81px solid {rx.color('slate', 5)}", - ), - class_name="rounded-md overflow-hidden", - ), - class_name="w-full flex flex-col rounded-md cursor-pointer", -) -``` - - -Video uploads are not currently supported but are coming soon! \ No newline at end of file diff --git a/docs/ai_builder/integrations/mcp_installation.md b/docs/ai_builder/integrations/mcp_installation.md deleted file mode 100644 index 41fb1d216..000000000 --- a/docs/ai_builder/integrations/mcp_installation.md +++ /dev/null @@ -1,75 +0,0 @@ -# Installation - -```python exec -import reflex as rx -``` - -```md alert warning -# The Reflex MCP integration is currently only available for enterprise customers. Please [book a demo](https://reflex.dev/pricing/) to discuss access. -``` - -To use the Reflex MCP integration, you'll need to configure your AI assistant or coding tool to connect to the Reflex MCP server. No additional Python packages are required on your local machine - the server is hosted and ready to use. - -## Prerequisites - -- An MCP-compatible AI tool (Claude Desktop, Windsurf, Codex, etc.) -- Internet connection to access the hosted MCP server -- Valid Reflex account for OAuth 2.1 authentication - -## Authentication - -The Reflex MCP server uses OAuth 2.1 protocol for secure authentication. You'll need a valid Reflex account, and authentication is handled automatically through your MCP client configuration when you provide your Reflex credentials. - -## IDE and Coding Assistant Integration - -### Claude Code - -Add the Reflex MCP server to Claude Code by running: - -```bash -claude mcp add --transport http reflex https://mcp.reflex.dev/mcp -``` - -Then authenticate by running the `/mcp` command inside Claude Code and following the login steps in your browser. Authentication tokens are stored securely and refreshed automatically. See the [Claude Code MCP documentation](https://code.claude.com/docs/en/mcp) for more details. - -### Claude Desktop - -Add the Reflex MCP server to your Claude Desktop configuration by editing your configuration file: - -```json -{ - "mcpServers": { - "reflex": { - "command": "npx", - "args": ["-y", "mcp-remote", "https://mcp.reflex.dev/mcp"] - } - } -} -``` - -### Windsurf/Cascade - -Create a `.vscode/mcp.json` file in your project root: - -```json -{ - "mcpServers": { - "reflex": { - "serverType": "http", - "url": "https://mcp.reflex.dev/mcp" - } - } -} -``` - -### Codex - -Add this configuration to your `~/.codex/config.toml` file: - -```toml -[mcp_servers.reflex] -command = "npx" -args = ["-y", "mcp-remote", "https://mcp.reflex.dev/mcp"] -``` - -Note: Codex requires MCP servers to communicate over stdio. The `mcp-remote` package bridges the connection to the HTTP-based Reflex MCP server. diff --git a/docs/ai_builder/integrations/mcp_overview.md b/docs/ai_builder/integrations/mcp_overview.md deleted file mode 100644 index f75b97cf9..000000000 --- a/docs/ai_builder/integrations/mcp_overview.md +++ /dev/null @@ -1,21 +0,0 @@ -# Overview - -```python exec -import reflex as rx -``` - -```md alert warning -# The Reflex MCP integration is currently only available for enterprise customers. Please [book a demo](https://reflex.dev/pricing/) to discuss access. -``` - -The Reflex [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) integration provides AI assistants and coding tools with structured access to Reflex framework documentation and component information. This enables intelligent assistance while developing Reflex applications. - -The Reflex MCP server is deployed at `https://mcp.reflex.dev/mcp` and provides access to component documentation and Reflex documentation through standardized MCP tools. - -## Available Tools - -The Reflex MCP server provides tools for accessing component documentation and Reflex documentation through standardized MCP capabilities. - -## Enterprise Use - -For enterprise customers requiring on-premises deployment of the Reflex MCP server, please [book a demo](https://reflex.dev/pricing/) to discuss your requirements. diff --git a/docs/ai_builder/overview/best_practices.md b/docs/ai_builder/overview/best_practices.md deleted file mode 100644 index 0c0eeb2fe..000000000 --- a/docs/ai_builder/overview/best_practices.md +++ /dev/null @@ -1,188 +0,0 @@ -# Reflex Build: Best Practices - -> A comprehensive guide to working effectively with AI Builder. This guide outlines how to get the most reliable and efficient results when working with the AI Builder inside Reflex Build. The key to success is clarity, structure, and iteration. - ---- - -## Core Workflow - -### Foundation: Planning Before You Build - -Before jumping into the AI Builder, take time to plan your approach. Good preparation leads to better results and fewer iterations. - -- **Define your core purpose and users** — Write a 2-3 sentence app description and identify your target users and their needs. -- **Prioritize 3-5 key features** — Focus on the most important functionality first, then expand from there. -- **Gather visual references** — Collect screenshots, wireframes, or sketches of layouts you want to emulate. -- **Structure your data** — List what information each page needs to display and how users will interact with it. -- **Start with your most important page** — Usually your main dashboard, home screen, or primary workflow. Get this right first. - -### Auto-Generate Prompts from App Specs - -To save time and get higher-quality prompts, you can feed your full app spec into the AI Builder and ask it to break the spec into structured, build-ready prompts. - -The AI Builder can translate your vision into: - -- Layout instructions -- UI component definitions -- Data model requirements -- Styling preferences -- Follow-up test plans - -**Example workflow:** - -1. Paste your full app specification into the AI Builder -2. Ask **"Break this into a series of buildable prompts."** -3. Execute each generated prompt in sequence -4. Build iteratively using the structured prompts - -### Working with Text Specifications - -If you have a structured app specification, don't paste the entire document into the builder at once. Break it down into logical sections and feed them in sequence. - -**Pro tip:** Let the AI Builder help prepare prompts: - -Paste your full app spec and ask: - - - **"Break this into buildable prompts."** - - **"Write one prompt per feature/page to build this app."** - -This approach — planning first, then building iteratively — lets you move faster and build smarter. - -### Writing Clear, Task-Oriented Prompts - -The AI performs best when it receives **specific, outcome-driven instructions**. Avoid vague, broad prompts. - - -```diff -- Build me an admin dashboard. # <- Bad -+ Create a 2-column layout with a sidebar for navigation and a top navbar. # <- Good -``` - -Whenever possible, split large tasks into smaller steps: - -- Define the layout first (columns, rows, sidebar) -- Add UI components (buttons, inputs, modals) -- Handle data models and states later -- Use follow-ups to style and polish - -Use precise styling language, for example: - -```diff -- Grid items have small spacing and sharp corners. -+ Add medium spacing between grid items and use large rounded corners on cards. -``` - -Avoid subjective terms like "**nice**," "**modern**," or "**clean**." Treat your prompt as interface documentation for the builder. - -### Working with Images and Visual References - -You can drop in screenshots of websites, dashboards, apps, or even hand-drawn wireframes. The builder will extract layout, design, and functionality ideas from these images. - -**Tips for images:** - -- Clear screenshots work best -- Include any elements you want: forms, tables, nav, charts -- You can annotate them with arrows, notes, or labels - -**Get UI/UX feedback:** - -Upload a screenshot and ask: **"What are 5 things I could do to improve the UI/UX of this?"** - -Follow up with: **Implement items 1, 2, and 4.** - -Or request specific improvements: **Make this more minimal and mobile-first.** - ---- - -## Optimizing Your Workflow - -### Building Iteratively - -Trying to generate your full app in a single prompt almost never works well. Instead, approach your build in clear stages: - -1. **Layout** — Grid, Flex, responsive columns/rows -2. **Components** — Tables, buttons, modals, charts, etc. -3. **State** — Bindings, stores, mock data -4. **Refinement** — Tweaks, visual polish, edge case handling - -At each stage, give feedback and iterate. If the AI builder makes something close, you can say: - -```diff -- Modal is included and sidebar is static. -+ Remove modal and make sidebar collapsible. - -- Buttons vary across pages. -+ Use same button style from home page. - -- Card layout differs across sections. -+ Repeat card layout from dashboard section. -``` - - -### Improving UI/UX - -To improve your design, ask the builder for more polished layouts, better structure, or more modern styles. - -```diff -- Layout feels cluttered with small headings and dense sections. -+ Increase heading sizes and spacing between sections for better hierarchy. - -- Navigation buttons are inconsistent. -+ Standardize button sizes and colors for consistent UX. -``` - -**Suggested workflow:** - -1. Upload an image or describe the layout. -2. Ask: **"Tell me 5 things that would improve the UI/UX of this page."** -3. Review the suggestions and decide which ones you want to apply. -4. Implement: **"Improve visual hierarchy by increasing heading sizes and adding more spacing between sections."** - -### Using Knowledge to Guide the Build - -The **Knowledge** panel lets you provide long-form references that influence how the agent builds your app. Add design systems, style guides, brand guidelines, or architecture rules. - -Once added, the builder will try to honor these rules throughout the session, ensuring consistency without repeating instructions. - -Try combining Knowledge with your prompts: - - - **Use the style guide in Knowledge to improve this page.** - - - **Is the current layout aligned with our design system in Knowledge?** - - -### Local Development Integration - -We have an MCP server available for enterprise customers to connect local AI development tools such as Claude Desktop, Windsurf, or Codex. - -This enables a hybrid workflow: generate your app and make major changes in the App Builder, then move to local development for detailed refinements and custom functionality. - -> **Enterprise Feature:** The Reflex MCP integration is currently only available for enterprise customers. [Book a demo](https://reflex.dev/pricing/) to discuss access. - -#### Quick Setup - -**Prerequisites:** - -- MCP-compatible AI tool (Claude Desktop, Windsurf, Codex) -- Valid Reflex account for OAuth authentication -- Internet connection to the hosted MCP server - -**Benefits:** - -- **Seamless handoff** — Move between web builder and local development -- **AI-powered local development** — Use your preferred AI tools with Reflex projects -- **No local installation** — Hosted MCP server requires no additional Python packages -- **Secure authentication** — OAuth 2.1 integration with your Reflex account - -For complete setup instructions for Claude Desktop, Windsurf, Codex, and other MCP clients, visit our [MCP integration](https://reflex.dev/docs/ai-builder/integrations/mcp-installation/) documentation. - ---- - -## Key Takeaways - -- **Plan before you build**. A few minutes of preparation saves hours of iteration. -- **Think modularly**. Focus on atomic parts before the full system. -- **Write like a designer-developer**. Clear, structural, and functional language wins. -- **Iterate continuously**. Let each prompt get you 80% there, then refine. - -With these techniques, the AI Builder becomes a reliable extension of your creative and technical intent. diff --git a/docs/ai_builder/overview/templates.md b/docs/ai_builder/overview/templates.md deleted file mode 100644 index 3a7bb09f6..000000000 --- a/docs/ai_builder/overview/templates.md +++ /dev/null @@ -1,43 +0,0 @@ -# Templates - -Reflex has many certified templates, seen on the `Trending` tab of the Reflex Build, that can be used to kickstart your app. - -```python exec -import reflex as rx -from pcweb.constants import REFLEX_ASSETS_CDN -from pcweb.components.image_zoom import image_zoom -``` - -```python eval -rx.el.div( - image_zoom( - rx.image( - src=f"{REFLEX_ASSETS_CDN}ai_builder/overview/templates_light.avif", - class_name="p-2 rounded-md h-auto", - border=f"0.81px solid {rx.color('slate', 5)}", - ), - class_name="rounded-md overflow-hidden", - ), - class_name="w-full flex flex-col rounded-md cursor-pointer", -) -``` - -## Using a Template - -To use a template, simply click the template and then in the bottom right corner of the app click the `Fork` button. This will create a copy of the template in your own account. You can then edit the app as you like with further prompting. - -```python eval -rx.el.div( - image_zoom( - rx.image( - src=f"{REFLEX_ASSETS_CDN}ai_builder/overview/fork_template_light.avif", - class_name="p-2 rounded-md h-auto", - border=f"0.81px solid {rx.color('slate', 5)}", - ), - class_name="rounded-md overflow-hidden", - ), - class_name="w-full flex flex-col rounded-md cursor-pointer", -) -``` - -Templates are great to get started if they have similar UI to what you are looking to build. You can then add your own data to the app. diff --git a/docs/ai_builder/overview/tutorial.md b/docs/ai_builder/overview/tutorial.md deleted file mode 100644 index 701e6580a..000000000 --- a/docs/ai_builder/overview/tutorial.md +++ /dev/null @@ -1,145 +0,0 @@ -# Your First Reflex Build App - -In this tutorial, you'll build a data dashboard application that displays employee information in both table and chart formats, with interactive features for filtering and adding new data. We'll also add a simple chatbot page to demonstrate multi-page navigation. - -## What You'll Build - -By the end of this tutorial, you'll have created: -- A dashboard displaying employee data in a table and bar chart -- Interactive filtering to search through your data -- A modal form for adding new employees -- A separate page with a simple chatbot interface - -This tutorial assumes you're starting with a new project in AI Builder. - ---- - -## Creating Your Dashboard - -Let's start by building the core of our application - a dashboard that displays employee data. - -**Prompt:** -``` -Create a dashboard page with a table showing sample employee data with columns: Name, Department, and Salary. Below the table, add a bar chart that visualizes the salary data. Include at least 5 sample employees with different departments and salary ranges. -``` - -This will create your main dashboard page with both tabular and visual representations of your data. The AI will generate sample employee records and create a bar chart that makes it easy to compare salaries across your team. - -```python exec -import reflex as rx -from pcweb.constants import REFLEX_ASSETS_CDN -from pcweb.components.image_zoom import image_zoom -``` - -```python eval -rx.el.div( - image_zoom( - rx.image( - src=f"{REFLEX_ASSETS_CDN}ai_builder/overview/tutorial_1_light.avif", - class_name="p-2 rounded-md h-auto", - border=f"0.81px solid {rx.color('slate', 5)}", - ), - class_name="rounded-md overflow-hidden", - ), - class_name="w-full flex flex-col rounded-md cursor-pointer", -) -``` - -## Adding Interactive Filtering - -Now that you have your basic dashboard, let's make it more interactive by adding the ability to filter and search through your employee data. - -**Prompt:** -``` -Add filtering functionality to the employee table. Include a search input above the table that filters rows based on name, and dropdown filters for department. Make sure the filters work together and update the table in real-time. -``` - -Your dashboard now becomes much more useful with real-time filtering. Users can quickly find specific employees by name or narrow down results by department. The filters work together, so you can combine a department filter with a name search. - -```python eval -rx.el.div( - image_zoom( - rx.image( - src=f"{REFLEX_ASSETS_CDN}ai_builder/overview/tutorial_2_light.avif", - class_name="p-2 rounded-md h-auto", - border=f"0.81px solid {rx.color('slate', 5)}", - ), - class_name="rounded-md overflow-hidden", - ), - class_name="w-full flex flex-col rounded-md cursor-pointer", -) -``` - - -## Enabling Data Entry - -A static dashboard is useful, but being able to add new data makes your app much more practical. Let's add the ability to create new employee records. - -**Prompt:** -``` -Add an "Add Employee" button above the table. When clicked, open a modal with input fields for Name, Department, and Salary. When the form is submitted, add the new employee to the table and update the bar chart. Include form validation for required fields. -``` - -Your app now has full CRUD capability for employee records. The modal form provides a clean interface for data entry, and both your table and chart update immediately when new employees are added. - - -```python eval -rx.el.div( - image_zoom( - rx.image( - src=f"{REFLEX_ASSETS_CDN}ai_builder/overview/tutorial_3_light.avif", - class_name="p-2 rounded-md h-auto", - border=f"0.81px solid {rx.color('slate', 5)}", - ), - class_name="rounded-md overflow-hidden", - ), - class_name="w-full flex flex-col rounded-md cursor-pointer", -) -``` - - -## Building a Multi-Page App - -Most real applications have multiple pages. Let's add a chatbot page to demonstrate navigation and create a more complete user experience. - -**Prompt:** -``` -Create a new page called "Chat" and add it to the navigation. Build a simple chatbot interface with a message input field, send button, and chat history display. For now, make the bot echo back the user's messages with "Bot says: [user message]". -``` - -Your app now has proper navigation between the dashboard and chat functionality. The chatbot page demonstrates how easy it is to add new features and pages to your AI Builder application. - - -```python eval -rx.el.div( - image_zoom( - rx.image( - src=f"{REFLEX_ASSETS_CDN}ai_builder/overview/tutorial_4_light.avif", - class_name="p-2 rounded-md h-auto", - border=f"0.81px solid {rx.color('slate', 5)}", - ), - class_name="rounded-md overflow-hidden", - ), - class_name="w-full flex flex-col rounded-md cursor-pointer", -) -``` - -## What's Next? - -You've successfully built a complete web application with data visualization, interactive filtering, data entry, and multi-page navigation. Your app demonstrates many common patterns used in modern web applications: - -- **Data presentation** with tables and charts -- **User interaction** through filtering and forms -- **Real-time updates** when data changes -- **Multi-page architecture** with navigation - -## Exploring Further - -Now that you have a working foundation, try experimenting with these ideas: - -- **Customize the data model** - change the employee fields or add new columns -- **Enhance the visualizations** - try different chart types or add more charts -- **Improve the chatbot** - give it more sophisticated responses or integrate it with your employee data -- **Add more pages** - create additional features like employee profiles or reporting dashboards - -The power of AI Builder is that you can iterate quickly with natural language prompts. Each new feature is just a conversation away! diff --git a/docs/ai_builder/overview/what_is_reflex_build.md b/docs/ai_builder/overview/what_is_reflex_build.md deleted file mode 100644 index effe63413..000000000 --- a/docs/ai_builder/overview/what_is_reflex_build.md +++ /dev/null @@ -1,242 +0,0 @@ -# What Is Reflex Build - -Reflex Build is an AI-powered platform that lets anyone create full-stack web apps just by describing ideas in plain English—no coding needed. It includes a full-fledged built-in IDE, real-time collaboration, and project sharing—all in your browser, no installation required. - -```python exec -import reflex as rx -from pcweb.components.image_zoom import image_zoom - -from pcweb.constants import REFLEX_ASSETS_CDN - - -landing_features = [ - { - "title": "Database Integration", - "description": "Automatically integrate your database\ninto your application with ease", - "icon": "database", - }, - { - "title": "Secure Secrets", - "description": "Safely manage your API keys and tokens\nwith a built in secrets manager", - "icon": "shield", - }, - { - "title": "Live Preview", - "description": "See all application changes in real-time\nwith our interactive preview tab", - "icon": "eye", - }, - { - "title": "Quick Download", - "description": "Download your complete project files\nwith just a single click operation", - "icon": "download", - }, - { - "title": "Easy Deployment", - "description": "Deploy your application to production\nwith just a single click process", - "icon": "rocket", - }, - { - "title": "Manual File Editing", - "description": "Edit your project files directly\nwith our intuitive code editor", - "icon": "code", - }, - { - "title": "AI Package Manager", - "description": "Let AI handle your package installations\nvia natural prompting", - "icon": "sparkles", - }, - { - "title": "Smart Prompting", - "description": "Get better development results\nwith AI-optimized prompt templates", - "icon": "message-circle", - }, -] - - -features_data = [ - { - "title": "Project Menu Bar", - "subtitle": "Browse previously built applications, create new sessions, store database variables, and much more!", - "img": f"{REFLEX_ASSETS_CDN}ai_builder/what_is_reflex_build/project_bar_light.avif", - }, - { - "title": "Chat Area", - "subtitle": "See your prompts in action with visual cues, editing notifications, and file generations every step of the way.", - "img": f"{REFLEX_ASSETS_CDN}ai_builder/what_is_reflex_build/chat_light.avif", - }, - { - "title": "Application Workspace", - "subtitle": "Your workspace contains all the folders and files of your application. You can add new files and folders as well!", - "img": f"{REFLEX_ASSETS_CDN}ai_builder/what_is_reflex_build/file_tree_light.avif", - }, - { - "title": "Code Editor", - "subtitle": "The code editor displays the current selected file. You can edit the code directly and save it instantly.", - "img": f"{REFLEX_ASSETS_CDN}ai_builder/what_is_reflex_build/code_light.avif", - }, - { - "title": "Integrations", - "subtitle": "Easily connect with the tools your team already uses or extend your app with any Python SDK, library, or API.", - "img": f"{REFLEX_ASSETS_CDN}ai_builder/what_is_reflex_build/integrations_light.avif", - }, - { - "title": "Plan", - "subtitle": "Plan your application's development with the AI Builder. You can add or remove phases and tasks as you go.", - "img": f"{REFLEX_ASSETS_CDN}ai_builder/what_is_reflex_build/plan_light.avif", - }, - { - "title": "Top Menu Bar", - "subtitle": "This menu contains the main views of the application. Preview, Code, Plan, Integrations, Knowledge, Secrets and Settings. You can also see the current workspace RAM and CPU usage. Deploy, copy or share your application with the buttons in the top right corner.", - "img": f"{REFLEX_ASSETS_CDN}ai_builder/what_is_reflex_build/top_light.avif", - }, - { - "title": "Preview Tab", - "subtitle": "The preview tab showcases a live application. You can navigate to other applications directly from this tab, refresh the app, and even view it in full screen.", - "img": f"{REFLEX_ASSETS_CDN}ai_builder/what_is_reflex_build/preview_light.avif", - }, -] - - -def feature_card(feature: dict) -> rx.Component: - return rx.el.div( - rx.el.div( - rx.el.span( - rx.icon( - tag=feature["icon"], - size=15, - class_name="inline-block mr-2 text-primary-11" - ), - rx.el.span(f"{feature['title']}"), - class_name="text-sm font-semibold flex flex-row items-center pt-5 px-2 text-secondary-12", - ), - rx.el.span( - feature["description"], - class_name="text-sm font-medium block align-center px-2 text-secondary-11", - ), - class_name="flex flex-col gap-2", - ), - class_name="w-full rounded-md", - ) - - -def _docs_features() -> rx.Component: - return rx.el.div( - rx.el.div( - rx.foreach(landing_features, feature_card), - class_name="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-2 lg:grid-cols-2 gap-4", - ), - class_name="flex flex-col w-full h-full justify-start align-start items-start py-4 gap-x-4 z-[99]", - ) - - -def _docs_app_section_features_small_screen(feature: dict): - return rx.el.div( - image_zoom( - rx.image( - src=feature["img"], - class_name="p-2 rounded-md h-auto", - border=f"0.81px solid {rx.color('slate', 5)}", - ), - class_name="rounded-md overflow-hidden", - ), - rx.el.div( - rx.el.label(feature["title"], class_name="text-sm font-bold cursor-pointer"), - rx.el.label(feature["subtitle"], class_name="text-sm font-light cursor-pointer"), - class_name="flex flex-col px-1 py-2", - ), - class_name="w-full flex flex-col rounded-md cursor-pointer", - ) - - -def _docs_app_section_toggles(feature: dict): - return rx.el.div( - rx.el.label(feature["title"], class_name="text-sm font-bold"), - rx.el.label( - feature["subtitle"], class_name="text-sm font-light" - ), - class_name="w-full flex flex-col max-w-md rounded-md p-4", - ) - - -def _docs_app_sections(): - return rx.el.div( - rx.el.div( - rx.el.div( - rx.el.label( - "Small details, big impact", class_name="text-sm font-light" - ), - rx.el.label( - "Made With Exceptional Care", class_name="text-3xl font-bold" - ), - rx.el.label( - "Every feature in Reflex Build is carefully crafted to set new standards. Mediocre isn't an option.", - class_name="text-md font-regular", - ), - class_name="flex flex-col w-full max-w-lg gap-y-1", - ), - rx.foreach( - features_data[:5], - lambda feature: _docs_app_section_toggles(feature), - ), - class_name="flex flex-col gap-y-4 justify-start max-w-sm", - ), - rx.el.div( - image_zoom( - rx.image( - src=features_data[0]["img"], - class_name="p-2 rounded-md h-auto", - border=f"0.81px solid {rx.color('slate', 5)}", - ), - class_name="rounded-md overflow-hidden", - ), - class_name="w-full max-w-4xl", - ), - class_name="flex flex-row w-full h-full justify-between align-center items-center py-4 gap-x-4 z-[99]", - display=["none" if i <= 4 else "flex" for i in range(6)], - ) - - -def _docs_app_sections_small_screen(): - return rx.el.div( - rx.el.div( - rx.grid( - rx.foreach( - features_data, - lambda feature: _docs_app_section_features_small_screen(feature), - ), - class_name="grid grid-cols-1 sm:grid-cols-1 md:grid-cols-2 lg:grid-cols-2 gap-10 w-full", - ), - class_name="flex flex-col gap-y-4 justify-start py-4", - ), - ) - - - - - - -screen_normalization = ( - "z-[99] w-full" - ) -``` - - -## Feature Overview - -Reflex Build provides a streamlined interface for building AI applications. The **Project Menu Bar** helps you manage sessions and stored variables, while the **Chat Area** displays real-time prompts, edits, and file generations. The **Application Workspace** organizes your project structure, and the **Code Editor** allows direct, instant code editing. Key actions like deploy and share are accessible via the **Bottom Menu Bar**, and the **Preview Tab** lets you view and interact with your live app at any time. - -```python eval -rx.el.div( - _docs_app_sections_small_screen(), -) -``` - -## Interface Highlights - -Reflex Build’s interface is designed for clarity and efficiency. The **Project Menu Bar** helps you manage sessions, apps, and variables. The **Chat Area** shows prompts in action with visual feedback and file generation. In the **Application Workspace**, you can view and organize your project files. The **Code Editor** allows quick, direct edits with instant saving. Use the **Bottom Menu Bar** for key actions like deploy and download. The **Preview Tab** lets you interact with a live version of your app, including refresh and full-screen options. - -```python eval -rx.el.div( - rx.el.div(_docs_features(), class_name=screen_normalization), -) -``` diff --git a/docs/ai_builder/python_libraries.md b/docs/ai_builder/python_libraries.md deleted file mode 100644 index 92200518f..000000000 --- a/docs/ai_builder/python_libraries.md +++ /dev/null @@ -1,43 +0,0 @@ -# Python Libraries - - -Not every service or tool has a first-class integration — but your app can still connect to **any Python library** directly. By leveraging the built-in Python runtime, the AI Builder can install packages, import libraries, and call their functions from workflows or components. This gives you maximum flexibility to extend your app with the broader Python ecosystem. - -When you ask for a certain functionality the AI Builder will first check if there is a `first-class integration` available. If not, it will `search the web` to try and find a relevant Python library to fulfill your request. If it finds one, it will install the package and ask you to set any `API keys` that are required. - - -## Example Use Cases - -### Slack - -There is no built-in integration for Slack. But if you ask the AI Builder to send a message to a Slack channel, it will research itself the best implementation and then use the `slack_sdk` Python package to send the message. - - -### Scikit-learn - -There is no built-in integration for Scikit-learn. But if you ask the AI Builder to classify some text using scikit-learn, it will research itself the best implementation and then use the `scikit-learn` Python package to load a pre-trained model and classify the text. - - - - -## Adding Custom Knowledge - -If you are working with a specialized / less well-known library, you can add custom knowledge to help the AI Builder understand how to use it. Simply provide a brief description of the library, its purpose, and example usage in the **Knowledge** section of your app settings. This will guide the AI Builder when it attempts to call functions from that library. - -```python exec -import reflex as rx -from pcweb.constants import REFLEX_ASSETS_CDN -``` - -```md alert warning -# Where to find the Knowledge Section -``` - -```python eval -rx.image(src=f"{REFLEX_ASSETS_CDN}ai_builder/features/knowledge_light.avif", alt="Where to find the Knowledge Section", class_name="rounded-lg border border-secondary-a4 mb-2") -``` - - -## What is Not Supported - -There are a very small number of libraries that are not supported due to their size. For example, large machine learning frameworks like `torch` or `tensorflow` are not supported directly. In these cases, we recommend using a first-class integration that can emulate similar functionality (e.g., the Replicate integration for running ML models in the cloud). \ No newline at end of file diff --git a/docs/ai_builder/urls.md b/docs/ai_builder/urls.md deleted file mode 100644 index 2341dd1b6..000000000 --- a/docs/ai_builder/urls.md +++ /dev/null @@ -1,31 +0,0 @@ -# URLs - -When you **paste a URL directly into the AI Builder chat**, the AI will automatically decide how to handle it depending on your prompt. - -You can use URLs to **copy a page’s design** or **extract its content**, without needing to set up any integration. - -## How It Works - -* If you say something like **“copy the design”** or **“use this layout”**, the AI will: - - * Take a **screenshot** of the page. - * Use it as a **visual reference** to recreate the UI in your app. - * Allow you to **customize** the generated design afterward. - -* 🪄 If you say something like **“get the content”**, **“scrape this page”**, or just paste the URL without mentioning design, the AI will: - - * **Scrape the content** of the page (text, links, images, metadata). - * Return it as structured data that can be used in components, workflows, or AI actions. - -## Example Prompts - -* “Copy the design of this page.” -* “Scrape the content from this blog post.” -* “Get all the product details from this URL.” -* (Paste the URL alone) → AI will assume content scraping by default. - -## Notes - -* **Public pages only:** The AI can only process URLs that are publicly accessible. -* **Editable:** Both the generated design and scraped content can be modified after processing. - diff --git a/docs/ai_builder/webhooks.md b/docs/ai_builder/webhooks.md deleted file mode 100644 index 7a64a70f2..000000000 --- a/docs/ai_builder/webhooks.md +++ /dev/null @@ -1,44 +0,0 @@ -# Webhooks - -Webhooks allow your app to **send data to external services** in real time. You provide the AI Builder with a **webhook URL** created in another platform, and it can **automatically send payloads** to that URL when workflows are triggered. - -This is a simple and powerful way to integrate with services that support incoming webhooks, even if there’s no first-class integration. - -## What You Can Do - -With outgoing webhooks, your app can: - -* **Send structured payloads** to any service that supports incoming webhooks (e.g., Slack, Zapier, Make, Discord). -* **Trigger external workflows** when events happen in your app. -* **Push real-time updates** to third-party systems without writing custom backend code. -* Chain webhook calls with other integrations or AI actions. - -## Step 1: Create a Webhook in the External Service - -1. Go to the external service you want to connect (e.g., Slack, Zapier, Discord, Make). -2. Create a new **incoming webhook** in that service. -3. Copy the **webhook URL** it provides. - - -## Step 2: Add the Webhook URL to AI Builder - -1. In the AI Builder chat paste the **webhook URL** you created in the external service. -2. You can then instruct the AI to **send data to this URL** whenever a workflow is triggered. -3. Optionally define the **payload structure** (e.g., JSON body) and when it should be sent. - -> 💡 Example: “Send user signup data to this webhook whenever a user signs up.” - -## Step 3: Sending Payloads - -* The AI Builder will write the code to send a `POST` request to the webhook URL with the payload you define. -* The payload can include **dynamic data** from your app — such as user info, state variables, or model outputs. -* You can trigger these webhook calls from buttons, events, workflows, or automations. - - -## Step 4: Notes - -* **Security:** Only use webhook URLs from trusted services — they will receive your data as-is. -* **Formatting:** Make sure the payload matches the expected format of the external service. -* **Chaining:** You can use multiple webhooks or combine them with other integrations. -* **Use cases:** Slack alerts, CRM updates, triggering automations in Zapier or Make, notifying custom systems. - diff --git a/docs/enterprise/ag_chart.md b/docs/enterprise/ag_chart.md deleted file mode 100644 index 30fdc4b53..000000000 --- a/docs/enterprise/ag_chart.md +++ /dev/null @@ -1,30 +0,0 @@ -# AG Chart - -AG Chart is a powerful charting library that provides interactive charts and data visualization components for enterprise applications. - -```python demo exec -import reflex as rx -import reflex_enterprise as rxe - -def basic_chart(): - return rxe.ag_chart( - options={ - "data": [ - {"month": "Jan", "value": 10}, - {"month": "Feb", "value": 20}, - {"month": "Mar", "value": 15}, - ], - "series": [ - { - "type": "line", - "xKey": "month", - "yKey": "value", - } - ], - }, - width="100%", - height="400px", - ) -``` - -For more detailed documentation, see the [AG Chart Documentation](https://charts.ag-grid.com/). diff --git a/docs/enterprise/ag_grid/aligned-grids.md b/docs/enterprise/ag_grid/aligned-grids.md deleted file mode 100644 index da63bef7f..000000000 --- a/docs/enterprise/ag_grid/aligned-grids.md +++ /dev/null @@ -1,65 +0,0 @@ ---- -title: Aligned Grids ---- - -AgGrid provides a way to align multiple grids together. This is useful when you want to display related data in a synchronized manner. - -You can do so through the `aligned_grids` prop. This prop takes a list of grid IDs that you want to align. - -```python demo exec -import pandas as pd -import reflex as rx -import reflex_enterprise as rxe - -# Olympic winners data (originally from https://www.ag-grid.com/example-assets/olympic-winners.json) -df = pd.read_json("data/olympic-winners.json") - -row_data = df.to_dict("records") - -column_defs = [ - {"field": "athlete"}, - {"field": "age"}, - {"field": "country"}, - {"field": "year"}, - {"field": "sport"}, - { - "header_name": "Medals", - "children": [ - { - "field": "total", - "column_group_show": "closed", - "col_id": "total", - "value_getter": "params.data.gold + params.data.silver + params.data.bronze", - "width": 100, - }, - {"field": "gold", "column_group_show": "open", "width": 100}, - {"field": "silver", "column_group_show": "open", "width": 100}, - {"field": "bronze", "column_group_show": "open", "width": 100}, - ], - }, -] - -def aligned_grids_page(): - """Aligned grids demo.""" - return rx.el.div( - rxe.ag_grid( - id="grid1", - column_defs=column_defs, - row_data=row_data, - aligned_grids=["grid2"], - width="100%", - ), rxe.ag_grid( - id="grid2", - column_defs=column_defs, - row_data=row_data, - aligned_grids=["grid1"], - width="100%", - ), - class_name="flex flex-col gap-y-6 w-full" - ) - -``` - -```md alert warning -# The pivot functionality does not work with aligned grids. This is because pivoting data changes the columns, which would make the aligned grids incompatible, as they are no longer sharing the same set of columns. -``` diff --git a/docs/enterprise/ag_grid/cell-selection.md b/docs/enterprise/ag_grid/cell-selection.md deleted file mode 100644 index c428d46fd..000000000 --- a/docs/enterprise/ag_grid/cell-selection.md +++ /dev/null @@ -1,288 +0,0 @@ ---- -title: "Cell Selection" -order: 8 ---- - -```python exec -from pcweb.pages.docs import enterprise -``` - -# Cell Selection - -AG Grid provides powerful cell selection capabilities that allow users to select individual cells or ranges of cells. This feature is essential for data manipulation, copying, and advanced interactions like fill handle operations. - -## Range Selection - -To enable cell selection in your AG Grid, set the `cell_selection` prop to `True`. This automatically enables both single cell selection and range selection capabilities. - -### Basic Selection Example - -```python demo exec -import reflex as rx -import reflex_enterprise as rxe -import pandas as pd - -class CellSelectionState(rx.State): - data: list[dict] = [] - - @rx.event - def load_data(self): - df = pd.read_json("https://www.ag-grid.com/example-assets/olympic-winners.json") - self.data = df.head(10).to_dict("records") - - @rx.event - def echo_selection(self, ranges: list[dict], started: bool, finished: bool): - if finished: - yield rx.toast(f"Selected ranges: {ranges}") - -column_defs = [ - {"field": "athlete", "width": 150}, - {"field": "age", "width": 90}, - {"field": "country", "width": 120}, - {"field": "year", "width": 90}, - {"field": "sport", "width": 120}, - {"field": "gold", "width": 100}, - {"field": "silver", "width": 100}, - {"field": "bronze", "width": 100}, -] - -def basic_cell_selection(): - return rx.vstack( - rx.text("Click and drag to select cells. Selection info will appear in a toast.", size="2"), - rxe.ag_grid( - id="basic_cell_selection_grid", - column_defs=column_defs, - row_data=CellSelectionState.data, - cell_selection=True, - on_cell_selection_changed=CellSelectionState.echo_selection, - width="100%", - height="400px", - ), - on_mount=CellSelectionState.load_data, - width="100%", - ) -``` - -### Advanced Selection Event Handling - -For more sophisticated selection handling, you can process the selection ranges to calculate detailed information: - -```python demo exec -import reflex as rx -import reflex_enterprise as rxe -import pandas as pd - -class AdvancedSelectionState(rx.State): - data: list[dict] = [] - - @rx.event - def load_data(self): - df = pd.DataFrame({ - "name": ["Alice", "Bob", "Charlie", "Diana", "Eve"], - "score": [85, 92, 78, 96, 88], - "grade": ["B", "A", "C", "A", "B"], - "attempts": [3, 2, 4, 1, 3] - }) - self.data = df.to_dict("records") - - @rx.event - def handle_selection(self, ranges: list[dict], started: bool, finished: bool): - if finished and ranges: - total_cells = sum( - (r.get("endRow", 0) - r.get("startRow", 0) + 1) * - len(r.get("columns", [])) - for r in ranges - ) - yield rx.toast(f"Selected {total_cells} cells across {len(ranges)} ranges") - -editable_column_defs = [ - {"field": "name", "width": 120}, - {"field": "score", "width": 100, "editable": True}, - {"field": "grade", "width": 100, "editable": True}, - {"field": "attempts", "width": 120, "editable": True}, -] - -def advanced_selection_example(): - return rx.vstack( - rx.text("Select ranges of cells. Try selecting multiple ranges by holding Ctrl/Cmd.", size="2"), - rxe.ag_grid( - id="advanced_selection_grid", - column_defs=editable_column_defs, - row_data=AdvancedSelectionState.data, - cell_selection=True, - on_cell_selection_changed=AdvancedSelectionState.handle_selection, - width="100%", - height="300px", - ), - on_mount=AdvancedSelectionState.load_data, - width="100%", - ) -``` - -## Fill Handle - -The fill handle is a powerful feature that allows users to quickly fill cells by dragging from a selected cell or range. When enabled, a small square appears at the bottom-right corner of the selection that users can drag to fill adjacent cells. - -### Enabling Fill Handle - -To enable the fill handle, configure the `cell_selection` prop with a dictionary containing the handle configuration: - -```python -cell_selection={ - "handle": { - "mode": "fill", # Enable fill handle - } -} -``` - -### Fill Handle Events - -When using the fill handle, it will trigger `on_cell_value_changed` for each cell receiving a fill value. This allows your backend to handle the data changes appropriately. - -```python demo exec -import reflex as rx -import reflex_enterprise as rxe -import pandas as pd - -class FillHandleState(rx.State): - data: list[dict] = [] - change_log: list[str] = [] - - @rx.event - def load_data(self): - df = pd.DataFrame({ - "item": ["Apple", "Banana", "Cherry", "Date", "Elderberry"], - "quantity": [10, 15, 8, 12, 20], - "price": [1.50, 0.75, 2.00, 3.00, 4.50], - "total": [15.00, 11.25, 16.00, 36.00, 90.00] - }) - self.data = df.to_dict("records") - - @rx.event - def handle_cell_change(self, data: dict): - row_index = data.get("rowIndex", 0) - field = data.get("colId", "") - new_value = data.get("newValue", "") - old_value = data.get("oldValue", "") - - change_msg = f"Row {row_index + 1}, {field}: '{old_value}' → '{new_value}'" - self.change_log = [change_msg] + self.change_log[:9] # Keep last 10 changes - - # Update the data - if 0 <= row_index < len(self.data): - self.data[row_index][field] = new_value - -fill_column_defs = [ - {"field": "item", "width": 120}, - {"field": "quantity", "width": 100, "editable": True, "type": "numericColumn"}, - {"field": "price", "width": 100, "editable": True, "type": "numericColumn"}, - {"field": "total", "width": 100, "editable": True, "type": "numericColumn"}, -] - -def fill_handle_example(): - return rx.vstack( - rx.text("Select a cell and drag the fill handle (small square at bottom-right) to fill adjacent cells.", size="2"), - rxe.ag_grid( - id="fill_handle_grid", - column_defs=fill_column_defs, - row_data=FillHandleState.data, - cell_selection={ - "handle": { - "mode": "fill", # Enable fill handle - } - }, - on_cell_value_changed=FillHandleState.handle_cell_change, - width="100%", - height="300px", - ), - rx.divider(), - rx.text("Recent Changes:", weight="bold", size="3"), - rx.cond( - FillHandleState.change_log, - rx.vstack( - rx.foreach( - FillHandleState.change_log, - lambda change: rx.text(change, size="1", color="gray") - ), - spacing="1", - ), - rx.text("No changes yet", size="2", color="gray") - ), - on_mount=FillHandleState.load_data, - width="100%", - spacing="4", - ) -``` - -## Advanced Configuration Options - -You can further customize cell selection behavior using additional configuration options: - -```python demo exec -import reflex as rx -import reflex_enterprise as rxe -import pandas as pd - -class ConfigurationState(rx.State): - data: list[dict] = [] - - @rx.event - def load_data(self): - df = pd.DataFrame({ - "id": range(1, 8), - "name": ["Product A", "Product B", "Product C", "Product D", "Product E", "Product F", "Product G"], - "category": ["Electronics", "Clothing", "Electronics", "Books", "Clothing", "Electronics", "Books"], - "price": [299.99, 49.99, 199.99, 24.99, 79.99, 399.99, 19.99], - "stock": [15, 32, 8, 45, 23, 12, 67] - }) - self.data = df.to_dict("records") - -configuration_column_defs = [ - {"field": "id", "width": 80}, - {"field": "name", "width": 150, "editable": True}, - {"field": "category", "width": 120}, - {"field": "price", "width": 100, "editable": True, "type": "numericColumn"}, - {"field": "stock", "width": 100, "editable": True, "type": "numericColumn"}, -] - -def configuration_example(): - return rx.vstack( - rx.text("Cell selection with additional configuration options", size="2"), - rxe.ag_grid( - id="configuration_grid", - column_defs=configuration_column_defs, - row_data=ConfigurationState.data, - cell_selection={ - "handle": { - "mode": "fill", - } - }, - enable_cell_text_selection=True, # Allow text selection within cells - suppress_cell_focus=False, # Allow cell focus - width="100%", - height="350px", - ), - on_mount=ConfigurationState.load_data, - width="100%", - ) -``` - -## Key Features - -- **Cell Selection**: Enable with `cell_selection=True` for both single cell and range selection capabilities -- **Fill Handle**: Configure with `cell_selection={"handle": {"mode": "fill"}}` for drag-to-fill functionality -- **Event Handling**: Use `on_cell_selection_changed` to respond to selection changes -- **Value Changes**: Use `on_cell_value_changed` to handle individual cell edits and fill operations -- **Text Selection**: Enable `enable_cell_text_selection=True` to allow text selection within cells - -## Best Practices - -1. **Use cell_selection configuration**: Both single cell and range selection are automatically enabled with `cell_selection=True`, providing all necessary selection capabilities for fill operations. - -2. **Handle cell value changes**: When using fill handle, implement `on_cell_value_changed` to process the data updates in your backend. - -3. **Provide user feedback**: Use toasts or other UI elements to confirm selection actions and data changes. - -4. **Consider performance**: For large datasets, be mindful of the performance impact of frequent cell value change events. - -5. **Validate fill operations**: Implement validation logic in your `on_cell_value_changed` handler to ensure data integrity. diff --git a/docs/enterprise/ag_grid/column-defs.md b/docs/enterprise/ag_grid/column-defs.md deleted file mode 100644 index a1dea0181..000000000 --- a/docs/enterprise/ag_grid/column-defs.md +++ /dev/null @@ -1,35 +0,0 @@ ---- -order: 1 ---- - -```python exec -from pcweb.pages.docs import enterprise -``` - -# Column Definitions - -## Basic Columns - -AgGrid allows you to define the columns of your grid, passed to the prop `column_defs`. Each dictionary represents a column. - -```md alert warning -# If you are converting from other AG Grid implementation, we also support camelCase for the name of the properties. -``` - -Here we define a grid with 3 columns: -```python -column_defs = [ - {"field": "direction"}, - {"field": "strength"}, - {"field": "frequency"}, -] -``` - -To set default properties for all your columns, you can define `default_col_def` in your grid: -```python -default_col_def = { - "sortable": True, - "filter": True, - "resizable": True, -} -``` \ No newline at end of file diff --git a/docs/enterprise/ag_grid/index.md b/docs/enterprise/ag_grid/index.md deleted file mode 100644 index a6ed8c25b..000000000 --- a/docs/enterprise/ag_grid/index.md +++ /dev/null @@ -1,681 +0,0 @@ ---- -title: "AgGrid Overview" -order: 3 ---- - -```python exec -from pcweb.pages.docs import enterprise -``` - -# AG Grid - -AG Grid is a powerful, feature-rich data grid component that brings enterprise-grade table functionality to your Reflex applications. With support for sorting, filtering, pagination, row selection, and much more, AG Grid transforms how you display and interact with tabular data. - -[Explore the full AG Grid showcase and examples](https://aggrid.reflex.run/) - -## Your First Reflex AG Grid - -A basic Reflex AG Grid contains column definitions `column_defs`, which define the columns to be displayed in the grid, and `row_data`, which contains the data to be displayed in the grid. - -Each grid also requires a unique `id`, which is needed to uniquely identify the Ag-Grid instance on the page. If you have multiple grids on the same page, each grid must have a unique `id` so that it can be correctly rendered and managed. - -```python demo exec -import reflex as rx -import reflex_enterprise as rxe -import pandas as pd - - -df = pd.read_csv("data/wind_dataset.csv") - -column_defs = [ - {"field": "direction"}, - {"field": "strength"}, - {"field": "frequency"}, -] - -def ag_grid_simple(): - return rxe.ag_grid( - id="ag_grid_basic_1", - row_data=df.to_dict("records"), - column_defs=column_defs, - width="100%", - ) -``` - -📊 **Dataset source:** [wind_dataset.csv](https://raw.githubusercontent.com/plotly/datasets/master/wind_dataset.csv) - -The format of the data passed to the `row_data` prop is a list of dictionaries. Each dictionary represents a row in the grid as seen below. - -```python -[ - \{"direction": "N", "strength": "0-1", "frequency": 0.5\}, - \{"direction": "NNE", "strength": "0-1", "frequency": 0.6\}, - \{"direction": "NE", "strength": "0-1", "frequency": 0.5\}, -] -``` - -The previous example showed the `column_defs` written out in full. You can also extract the required information from the dataframe's column names: - -```python demo exec -import reflex as rx -import reflex_enterprise as rxe -import pandas as pd - -df = pd.read_csv("data/wind_dataset.csv") - - -def ag_grid_simple_2(): - return rxe.ag_grid( - id="ag_grid_basic_2", - row_data=df.to_dict("records"), - column_defs=[{"field": i} for i in df.columns], - width="100%", - height="40vh", - ) -``` - -📊 **Dataset source:** [wind_dataset.csv](https://raw.githubusercontent.com/plotly/datasets/master/wind_dataset.csv) - -## Headers - -In the above example, the first letter of the field names provided are capitalized when displaying the header name. You can customize the header names by providing a `header_name` key in the column definition. In this example, the `header_name` is customized for the second and third columns. - -```python demo exec -import reflex as rx -import reflex_enterprise as rxe -import pandas as pd - - -df = pd.read_csv("data/gapminder2007.csv") - -column_defs = [ - {"field": "country"}, - {"field": "pop", "headerName": "Population"}, - {"field": "lifeExp", "headerName": "Life Expectancy"}, -] - -def ag_grid_simple_headers(): - return rxe.ag_grid( - id="ag_grid_basic_headers", - row_data=df.to_dict("records"), - column_defs=column_defs, - width="100%", - height="40vh", - ) -``` - -📊 **Dataset source:** [gapminder2007.csv](https://raw.githubusercontent.com/plotly/datasets/master/gapminder2007.csv) - -## Column Filtering - -Allow a user to filter a column by setting the `filter` key to `True` in the column definition. In this example we enable filtering for the first and last columns. - -```python demo exec -import reflex as rx -import reflex_enterprise as rxe -import pandas as pd - - -df = pd.read_csv("data/gapminder2007.csv") - -column_defs = [ - {"field": "country", "headerName": "Country", "filter": True}, - {"field": "pop", "headerName": "Population"}, - {"field": "lifeExp", "headerName": "Life Expectancy", "filter": True}, -] - -def ag_grid_simple_column_filtering(): - return rxe.ag_grid( - id="ag_grid_basic_column_filtering", - row_data=df.to_dict("records"), - column_defs=column_defs, - width="100%", - height="40vh", - ) -``` - -### Filter Types - -You can set `filter=True` to enable the default filter for a column. - -You can also set the filter type using the `filter` key. The following filter types are available: `ag_grid.filters.date`, `ag_grid.filters.number` and `ag_grid.filters.text`. These ensure that the input you enter to the filter is of the correct type. - -(`ag_grid.filters.set` and `ag_grid.filters.multi` are available with AG Grid Enterprise) - -```python demo exec -import reflex as rx -import reflex_enterprise as rxe -import pandas as pd - - -df = pd.read_csv("data/GanttChart-updated.csv") - -column_defs = [ - {"field": "Task", "filter": True}, - {"field": "Start", "filter": rxe.ag_grid.filters.date}, - {"field": "Duration", "filter": rxe.ag_grid.filters.number}, - {"field": "Resource", "filter": rxe.ag_grid.filters.text}, -] - -def ag_grid_simple_column_filtering(): - return rxe.ag_grid( - id="ag_grid_basic_column_filtering", - row_data=df.to_dict("records"), - column_defs=column_defs, - width="100%", - height="40vh", - ) -``` - -📊 **Dataset source:** [GanttChart-updated.csv](https://raw.githubusercontent.com/plotly/datasets/master/GanttChart-updated.csv) - -## Row Sorting - -By default, the rows can be sorted by any column by clicking on the column header. You can disable sorting of the rows for a column by setting the `sortable` key to `False` in the column definition. - -In this example, we disable sorting for the first column. - -```python demo exec -import reflex as rx -import reflex_enterprise as rxe -import pandas as pd - - -df = pd.read_csv("data/gapminder2007.csv") - -column_defs = [ - {"field": "country", "sortable": False}, - {"field": "pop", "headerName": "Population"}, - {"field": "lifeExp", "headerName": "Life Expectancy"}, -] - -def ag_grid_simple_row_sorting(): - return rxe.ag_grid( - id="ag_grid_basic_row_sorting", - row_data=df.to_dict("records"), - column_defs=column_defs, - width="100%", - height="40vh", - ) -``` - -📊 **Dataset source:** [gapminder2007.csv](https://raw.githubusercontent.com/plotly/datasets/master/gapminder2007.csv) - -## Row Selection - -Row Selection is enabled using the `row_selection` attribute. Setting it to `multiple` allows users to select multiple rows at a time. You can use the `checkbox_selection` column definition attribute to render checkboxes for selection. - -```python demo exec -import reflex as rx -import reflex_enterprise as rxe -import pandas as pd - - -df = pd.read_csv("data/gapminder2007.csv") - -column_defs = [ - {"field": "country", "checkboxSelection": True}, - {"field": "pop", "headerName": "Population"}, - {"field": "continent"}, -] - -def ag_grid_simple_row_selection(): - return rxe.ag_grid( - id="ag_grid_basic_row_selection", - row_data=df.to_dict("records"), - column_defs=column_defs, - row_selection={"mode":"multiple"}, - width="100%", - height="40vh", - ) -``` - -📊 **Dataset source:** [gapminder2007.csv](https://raw.githubusercontent.com/plotly/datasets/master/gapminder2007.csv) - -## Editing - -Enable Editing by setting the `editable` attribute to `True`. The cell editor is inferred from the cell data type. Set the cell editor type using the `cell_editor` attribute. - -There are 7 provided cell editors in AG Grid: - -1. `ag_grid.editors.text` -2. `ag_grid.editors.large_text` -3. `ag_grid.editors.select` -4. `ag_grid.editors.rich_select` -5. `ag_grid.editors.number` -6. `ag_grid.editors.date` -7. `ag_grid.editors.checkbox` - -In this example, we enable editing for the second and third columns. The second column uses the `number` cell editor, and the third column uses the `select` cell editor. - -The `on_cell_value_changed` event trigger is linked to the `cell_value_changed` event handler in the state. This event handler is called whenever a cell value is changed and changes the value of the backend var `_data_df` and the state var `data`. - -```python -import reflex as rx -import reflex_enterprise as rxe -import pandas as pd - -class AGGridEditingState(rx.State): - data: list[dict] = [] - _data_df: pd.DataFrame - - @rx.event - def load_data(self): - self._data_df = pd.read_csv("data/gapminder2007.csv") - self.data = self._data_df.to_dict("records") - - @rx.event - def cell_value_changed(self, row, col_field, new_value): - self._data_df.at[row, col_field] = new_value - self.data = self._data_df.to_dict("records") - yield rx.toast(f"Cell value changed, Row: {row}, Column: {col_field}, New Value: {new_value}") - - -column_defs = [ - \{"field": "country"\}, - \{"field": "pop", "headerName": "Population", "editable": True, "cellEditor": rxe.ag_grid.editors.number\}, - \{"field": "continent", "editable": True, "cellEditor": rxe.ag_grid.editors.select, "cellEditorParams": \{"values": ['Asia', 'Europe', 'Africa', 'Americas', 'Oceania']\}\}, -] - -def ag_grid_simple_editing(): - return rxe.ag_grid( - id="ag_grid_basic_editing", - row_data=AGGridEditingState.data, - column_defs=column_defs, - on_cell_value_changed=AGGridEditingState.cell_value_changed, - on_mount=AGGridEditingState.load_data, - width="100%", - height="40vh", - ) -``` - -## Pagination - -By default, the grid uses a vertical scroll. You can reduce the amount of scrolling required by adding pagination. To add pagination, set `pagination=True`. You can set the `pagination_page_size` to the number of rows per page and `pagination_page_size_selector` to a list of options for the user to select from. - -```python demo exec -import reflex as rx -import reflex_enterprise as rxe -import pandas as pd - -df = pd.read_csv("data/gapminder2007.csv") - -column_defs = [ - {"field": "country"}, - {"field": "pop", "headerName": "Population"}, - {"field": "lifeExp", "headerName": "Life Expectancy"}, -] - -def ag_grid_simple_pagination(): - return rxe.ag_grid( - id="ag_grid_basic_pagination", - row_data=df.to_dict("records"), - column_defs=column_defs, - pagination=True, - pagination_page_size=10, - pagination_page_size_selector=[10, 40, 100], - width="100%", - height="40vh", - ) -``` - -📊 **Dataset source:** [gapminder2007.csv](https://raw.githubusercontent.com/plotly/datasets/master/gapminder2007.csv) - -## AG Grid with State - -### Putting Data in State - -Assuming you want to make any edit to your data, you can put the data in State. This allows you to update the grid based on user input. Whenever the `data` var is updated, the grid will be re-rendered with the new data. - -```python -from typing import Any -import reflex as rx -import reflex_enterprise as rxe -import pandas as pd - -class AGGridState2(rx.State): - data: list[dict] = [] - - @rx.event - def load_data(self): - _df = pd.read_csv("data/gapminder2007.csv") - self.data = _df.to_dict("records") - -column_defs = [ - \{"field": "country"\}, - \{"field": "pop", "headerName": "Population"\}, - \{"field": "continent"\}, -] - -def ag_grid_state_2(): - return rxe.ag_grid( - id="ag_grid_state_2", - row_data=AGGridState2.data, - column_defs=column_defs, - on_mount=AGGridState2.load_data, - width="100%", - height="40vh", - ) -``` - -### Updating the Grid with State - -You can use State to update the grid based on a users input. In this example, we update the `column_defs` of the grid when a user clicks a button. - -```python -import reflex as rx -import reflex_enterprise as rxe -import pandas as pd - -class AgGridState(rx.State): - """The app state.""" - all_columns: list = [] - - two_columns: list = [] - column_defs: list = all_columns - n_clicks = 0 - - @rx.event - def init_columns(self): - self.all_columns = [ - \{"field": "country"\}, - \{"field": "pop"\}, - \{"field": "continent"\}, - \{"field": "lifeExp"\}, - \{"field": "gdpPercap"\}, - ] - self.two_columns = [ - \{"field": "country"\}, - \{"field": "pop"\}, - ] - self.column_defs = self.all_columns - - @rx.event - def update_columns(self): - self.n_clicks += 1 - if self.n_clicks % 2 != 0: - self.column_defs = self.two_columns - else: - self.column_defs = self.all_columns - - -df = pd.read_csv("data/gapminder2007.csv") - - -def ag_grid_simple_with_state(): - return rx.box( - rx.button("Toggle Columns", on_click=AgGridState.update_columns), - rxe.ag_grid( - id="ag_grid_basic_with_state", - row_data=df.to_dict("records"), - column_defs=AgGridState.column_defs, - on_mount=AgGridState.init_columns, - width="100%", - height="40vh", - ), - width="100%", - ) -``` - -📊 **Dataset source:** [gapminder2007.csv](https://raw.githubusercontent.com/plotly/datasets/master/gapminder2007.csv) - -## AG Grid with Data from a Database - -In this example, we will use a database to store the data. The data is loaded from a csv file and inserted into the database when the page is loaded using the `insert_dataframe_to_db` event handler. - -The data is then fetched from the database and displayed in the grid using the `data` computed var. - -When a cell value is changed, the data is updated in the database using the `cell_value_changed` event handler. - -```python -import reflex as rx -import reflex_enterprise as rxe -import pandas as pd -from sqlmodel import select - -class Country(rx.Model, table=True): - country: str - population: int - continent: str - - -class AGGridDatabaseState(rx.State): - - countries: list[Country] - - # Insert data from a csv loaded dataframe to the database (Do this on the page load) - @rx.event - def insert_dataframe_to_db(self): - data = pd.read_csv("data/gapminder2007.csv") - with rx.session() as session: - for _, row in data.iterrows(): - db_record = Country( - country=row['country'], - population=row['pop'], - continent=row['continent'], - ) - session.add(db_record) - session.commit() - - # Fetch data from the database using a computed variable - @rx.var - def data(self) -> list[dict]: - with rx.session() as session: - results = session.exec(select(Country)).all() - self.countries = [result.dict() for result in results] - return self.countries - - # Update the database when a cell value is changed - @rx.event - def cell_value_changed(self, row, col_field, new_value): - self.countries[row][col_field] = new_value - with rx.session() as session: - country = Country(**self.countries[row]) - session.merge(country) - session.commit() - yield rx.toast(f"Cell value changed, Row: \{row}, Column: \{col_field}, New Value: \{new_value}") - - -column_defs = [ - \{"field": "country"\}, - \{"field": "population", "headerName": "Population", "editable": True, "cellEditor": rxe.ag_grid.editors.number\}, - \{"field": "continent", "editable": True, "cellEditor": rxe.ag_grid.editors.select, "cellEditorParams": \{"values": ['Asia', 'Europe', 'Africa', 'Americas', 'Oceania']\}\}, -] - -def index(): - return rxe.ag_grid( - id="ag_grid_basic_editing", - row_data=AGGridDatabaseState.data, - column_defs=column_defs, - on_cell_value_changed=AGGridDatabaseState.cell_value_changed, - width="100%", - height="40vh", - ) - -# Add state and page to the app. -app = rx.App() -app.add_page(index, on_load=AGGridDatabaseState.insert_dataframe_to_db) -``` - -## Using AG Grid Enterprise - -AG Grid offers both community and enterprise versions. See the [AG Grid docs](https://www.ag-grid.com/archive/31.2.0/react-data-grid/licensing/) for details on purchasing a license key. - -To use an AG Grid Enterprise license key with Reflex AG Grid set the environment variable `AG_GRID_LICENSE_KEY`: - -```bash -export AG_GRID_LICENSE_KEY="your_license_key" -``` - -## column_def props - -The following props are available for `column_defs` as well as many others that can be found here: [AG Grid Column Def Docs](https://www.ag-grid.com/react-data-grid/column-properties/). (it is necessary to use snake_case for the keys in Reflex, unlike in the AG Grid docs where camelCase is used) - -- `field`: `str`: The field of the row object to get the cell's data from. -- `col_id`: `str | None`: The unique ID to give the column. This is optional. If missing, the ID will default to the field. -- `type`: `str | None`: The type of the column. -- `cell_data_type`: `bool | str | None`: The data type of the cell values for this column. Can either infer the data type from the row data (true - the default behaviour), define a specific data type (string), or have no data type (false). -- `hide`: `bool`: Set to true for this column to be hidden. -- `editable`: `bool | None`: Set to true if this column is editable, otherwise false. -- `filter`: `AGFilters | str | None`: Filter component to use for this column. Set to true to use the default filter. Set to the name of a provided filter to use that filter. (Check out the Filter Types section of this page for more information) -- `floating_filter`: `bool`: Whether to display a floating filter for this column. -- `header_name`: `str | None`: The name to render in the column header. If not specified and field is specified, the field name will be used as the header name. -- `header_tooltip`: `str | None`: Tooltip for the column header. -- `checkbox_selection`: `bool | None`: Set to true to render a checkbox for row selection. -- `cell_editor`: `AGEditors | str | None`: Provide your own cell editor component for this column's cells. (Check out the Editing section of this page for more information) -- `cell_editor_params`: `dict[str, list[Any]] | None`: Params to be passed to the cellEditor component. - - - -## Functionality you need is not available/working in Reflex - -All AGGrid options found in this [documentation](https://www.ag-grid.com/react-data-grid/reference/) are mapped in rxe.ag_grid, but some features might not have been fully tested, due to the sheer number of existing features in the underlying AG Grid library. - -If one of the ag_grid props does not import the expected module, you can pass it manually via the props `community_modules` or `enterprise_modules`, which expect a `set[str]` of the module names. You will get a warning in the browser console if a module is missing, so you can check there if a feature is not working as expected. - -You can also report the missing module on our discord or GitHub issues page of the main Reflex repository. - -Best practice is to create a single instance of `ag_grid.api()` with the same `id` as the `id` of the `ag_grid` component that is to be referenced, `"ag_grid_basic_row_selection"` in this first example. - -The example below uses the `select_all()` and `deselect_all()` methods of the AG Grid API to select and deselect all rows in the grid. This method is not available in Reflex directly. Check out this [documentation](https://www.ag-grid.com/react-data-grid/grid-api/#reference-selection-selectAll) to see what the methods look like in the AG Grid docs. - -```md alert info -# Ensure that the docs are set to React tab in AG Grid -``` - -```python demo exec -import reflex as rx -import reflex_enterprise as rxe -import pandas as pd - -df = pd.read_csv( - "data/gapminder2007.csv" -) - -column_defs = [ - {"field": "country", "checkboxSelection": True}, - {"field": "pop"}, - {"field": "continent"}, -] - -def ag_grid_api_simple(): - my_api = rxe.ag_grid.api(id="ag_grid_basic_row_selection") - return rx.vstack( - rxe.ag_grid( - id="ag_grid_basic_row_selection", - row_data=df.to_dict("records"), - column_defs=column_defs, - row_selection="single", - width="100%", - height="40vh", - ), - rx.button("Select All", on_click=my_api.select_all()), - rx.button("Deselect All", on_click=my_api.deselect_all()), - spacing="4", - width="100%", - ) -``` - -📊 **Dataset source:** [gapminder2007.csv](https://raw.githubusercontent.com/plotly/datasets/master/gapminder2007.csv) - -The react code for the `select_all()` event handler is `selectAll = (source?: SelectionEventSourceType) => void;`. - -To use this in Reflex as you can see, it should be called in snake case rather than camel case. The `void` means it doesn't return anything. The `source?` indicates that it takes an optional `source` argument. - - -```md alert info -# Another way to use the AG Grid API -It is also possible to use the AG Grid API directly with the event trigger (`on_click`) of the component. This removes the need to create a variable `my_api`. This is shown in the example below. It is necessary to use the `id` of the `ag_grid` component that is to be referenced. - -```python -rx.button("Select all", on_click=rxe.ag_grid.api(id="ag_grid_basic_row_selection").select_all()), -``` - -### More examples - -The following example lets a user [export the data as a csv](https://www.ag-grid.com/javascript-data-grid/grid-api/#reference-export-exportDataAsCsv) and [adjust the size of columns to fit the available horizontal space](https://www.ag-grid.com/javascript-data-grid/grid-api/#reference-columnSizing-sizeColumnsToFit). (Try resizing the screen and then clicking the resize columns button) - - -```python demo exec -import reflex as rx -import reflex_enterprise as rxe -import pandas as pd - -df = pd.read_csv( - "data/gapminder2007.csv" -) - -column_defs = [ - {"field": "country", "checkboxSelection": True}, - {"field": "pop"}, - {"field": "continent"}, -] - -def ag_grid_api_simple2(): - my_api = rxe.ag_grid.api(id="ag_grid_export_and_resize") - return rx.vstack( - rxe.ag_grid( - id="ag_grid_export_and_resize", - row_data=df.to_dict("records"), - column_defs=column_defs, - width="100%", - height="40vh", - ), - rx.button("Export", on_click=my_api.export_data_as_csv()), - rx.button("Resize Columns", on_click=my_api.size_columns_to_fit()), - spacing="4", - width="100%", - ) -``` - -📊 **Dataset source:** [gapminder2007.csv](https://raw.githubusercontent.com/plotly/datasets/master/gapminder2007.csv) - -The react code for both of these is shown below. The key point to see is that both of these functions return `void` and therefore does not return anything. - -`exportDataAsCsv = (params?: CsvExportParams) => void;` - -`sizeColumnsToFit = (paramsOrGridWidth?: ISizeColumnsToFitParams | number) => void;` - - -### Example with a Return Value - -This example shows how to get the data from `ag_grid` as a [csv on the backend](https://www.ag-grid.com/javascript-data-grid/grid-api/#reference-export-getDataAsCsv). The data that was passed to the backend is then displayed as a toast with the data. - -```python -import reflex as rx -import reflex_enterprise as rxe -import pandas as pd - -class AGGridStateAPI(rx.State): - def handle_get_data(self, data: str): - yield rx.toast(f"Got CSV data: {data}") - -df = pd.read_csv( - "data/gapminder2007.csv" -) - -column_defs = [ - \{"field": "country", "checkboxSelection": True\}, - \{"field": "pop"\}, - \{"field": "continent"\}, -] - -def ag_grid_api_argument(): - my_api = rxe.ag_grid.api(id="ag_grid_get_data_as_csv") - return rx.vstack( - rxe.ag_grid( - id="ag_grid_get_data_as_csv", - row_data=df.to_dict("records"), - column_defs=column_defs, - width="100%", - height="40vh", - ), - rx.button("Get CSV data on backend", on_click=my_api.get_data_as_csv(callback=AGGridStateAPI.handle_get_data)), - spacing="4", - width="100%", - ) -``` - -The react code for the `get_data_as_csv` method of the AG Grid API is `getDataAsCsv = (params?: CsvExportParams) => string | undefined;`. Here the function returns a `string` (or undefined). - -In Reflex to handle this returned value it is necessary to pass a `callback` as an argument to the `get_data_as_csv` method that will get the returned value. In this example the `handle_get_data` event handler is passed as the callback. This event handler will be called with the returned value from the `get_data_as_csv` method. diff --git a/docs/enterprise/ag_grid/model-wrapper.md b/docs/enterprise/ag_grid/model-wrapper.md deleted file mode 100644 index 4615ee872..000000000 --- a/docs/enterprise/ag_grid/model-wrapper.md +++ /dev/null @@ -1,64 +0,0 @@ ---- -order: 6 ---- - -# Model Wrapper - -A model wrapper is an utility used to wrap a database model and provide a consistent interface over it. It allows automatically adding new rows to the database, updating existing rows, and deleting rows. - -## Default Model Wrapper - -You can use the basic functionality of the model wrapper by using the `rxe.model_wrapper` function. This function takes a database model and returns a wrapper object that can be used to interact with the model. - -```python -import reflex_enterprise as rxe - -def index_page(): - return rxe.model_wrapper(class_model=MyModel) -``` - -By default the model_wrapper use the infinite rows model from AgGrid. - -## Custom Model Wrapper - -If the default model wrapper does not fit your needs, you can create a custom model wrapper by subclassing the `rxe.ModelWrapper` class. This allows you to customize the behavior of the model wrapper to fit your specific use case. - -```python -import reflex_enterprise as rxe - -class MyCustomWrapper(rxe.ModelWrapper[MyModel]): - pass -``` - -In the custom model wrapper, you can override the following methods: -- `_get_columns_defs` -- `_get_data` -- `_row_count` -- `on_value_setter` - -to modify how the model wrapper will behave. - - -## SSRM Model Wrapper - -The SSRM model wrapper, used with `rxe.model_wrapper_ssrm`, is a version of the model wrapper that allows you to use the ServerSideRowModel of AgGrid. - -```python -import reflex_enterprise as rxe - -def index_page(): - return rxe.model_wrapper_ssrm(class_model=MyModel) -``` - -## SSRM Custom Model Wrapper - -In the same way you can extend the default model wrapper, you can extend the SSRM custom model wrapper by subclassing the `rxe.ModelWrapperSSRM` class. This allows you to customize the behavior of the model wrapper to fit your specific use case. - -```python -import reflex_enterprise as rxe - -class MyCustomSSRMWrapper(rxe.ModelWrapperSSRM[MyModel]): - pass -``` - -The overridable methods are the same as the standard model wrapper. \ No newline at end of file diff --git a/docs/enterprise/ag_grid/pivot-mode.md b/docs/enterprise/ag_grid/pivot-mode.md deleted file mode 100644 index f2f9b8635..000000000 --- a/docs/enterprise/ag_grid/pivot-mode.md +++ /dev/null @@ -1,130 +0,0 @@ -```python exec -import reflex as rx -import reflex_enterprise as rxe -from pcweb.pages.docs import enterprise -``` - -# Pivot Mode - -Pivot mode allows you to visualize your data in a different way than how they are originally structured in the data source. When pivoting on a column, the values in that column will be used as column headers. This allows you to see the data in a more compact way, and can be useful when you have a lot of data to display. - -To enable pivot mode, set the `pivot_mode` property to `True` in the grid props. Once pivot mode is enabled, you can define which column to pivot on by setting the `pivot` property in a column definition. In addition to the pivot column, at least one column definition must have `row_group` property set to `True` to define the row grouping. - -You can also define how rows are aggregated by passing the `agg_func` property in the column definition. The `agg_func` property should be set to a string that represents the aggregation function to use. The built-in aggregation functions are `sum`, `min`, `max`, `count`, `avg`, `first`, and `last`. - -You can find a live example here: [Pivot Mode Example](https://aggrid.reflex.run/pivot). - -```python demo exec -import pandas as pd -import reflex as rx - -import reflex_enterprise as rxe - -# Olympic winners data (originally from https://www.ag-grid.com/example-assets/olympic-winners.json) -df = pd.read_json("data/olympic-winners.json") - -def pivot_page(): - return rxe.ag_grid( - id="sandbox_grid", - column_defs=[ - {"field": "country", "row_group": True}, - {"field": "sport", "pivot": True}, - {"field": "year", "pivot": True}, - {"field": "gold", "aggFunc": "sum"}, - ], - loading=False, - row_data=df.to_dict("records"), - default_col_def={ - "flex": 1, - "min_width": 130, - "enable_value": True, - "enable_row_group": True, - "enable_pivot": True, - }, - auto_group_column_def={ - "minWidth": 200, - "pinned": "left", - }, - pivot_mode=True, - side_bar="columns", - pivot_panel_show="always", - width="100%", - height="500px", - ), -``` - -# Pivot using State - -```python demo exec -import pandas as pd -import reflex as rx - -import reflex_enterprise as rxe - -df = pd.read_csv("data/wind_dataset.csv") - - -class PivotState(rx.State): - """State for the sandbox page.""" - - pivot = False - row_grouping = False - - @rx.event - def toggle_pivot(self): - """Toggle the pivot.""" - self.pivot = not self.pivot - - @rx.event - def toggle_row_grouping(self): - """Toggle the row grouping.""" - self.row_grouping = not self.row_grouping - - -def sandbox_page(): - """Sandbox page.""" - return rx.vstack( - rx.hstack( - rx.text("Toggle Pivot"), - rx.switch( - on_click=PivotState.toggle_pivot, - name="pivot", - checked=PivotState.pivot, - ), - rx.text("Toggle Row Grouping"), - rx.switch( - on_click=PivotState.toggle_row_grouping, - name="row_grouping", - checked=PivotState.row_grouping, - ) - ), - rxe.ag_grid( - id="sandbox_grid", - column_defs=[ - rxe.ag_grid.column_def( - field="direction", - pivot=True, - ), - rxe.ag_grid.column_def( - field="strength", - ), - rxe.ag_grid.column_def( - field="frequency", - agg_func="count", - row_group=PivotState.row_grouping, - ), - ], - row_data=df.to_dict("records"), - pivot_mode=PivotState.pivot, - pivot_panel_show="onlyWhenPivoting", - width="100%", - height="500px", - ), - width="100%", - ) - -``` - -📊 **Dataset source:** [wind_dataset.csv](https://raw.githubusercontent.com/plotly/datasets/master/wind_dataset.csv) - - diff --git a/docs/enterprise/ag_grid/theme.md b/docs/enterprise/ag_grid/theme.md deleted file mode 100644 index f88fa3924..000000000 --- a/docs/enterprise/ag_grid/theme.md +++ /dev/null @@ -1,63 +0,0 @@ ---- -order: 3 ---- - -```python exec -import reflex as rx -import reflex_enterprise as rxe -from pcweb.pages.docs import enterprise -``` - -# Themes - -```md alert warning -# Only the old theme API of AG Grid is currently supported. The new theme API is not supported yet. -``` - -You can style your grid with a theme. AG Grid includes the following themes: - -1. `quartz` -2. `alpine` -3. `balham` -4. `material` - -The grid uses `quartz` by default. To use any other theme, set it using the `theme` prop, i.e. `theme="alpine"`. - -```python -import reflex as rx -import reflex_enterprise as rxe -import pandas as pd - -class AGGridThemeState(rx.State): - """The app state.""" - - theme: str = "quartz" - themes: list[str] = ["quartz", "balham", "alpine", "material"] - -df = pd.read_csv("data/gapminder2007.csv") - -column_defs = [ - {"field": "country"}, - {"field": "pop", "headerName": "Population"}, - {"field": "lifeExp", "headerName": "Life Expectancy"}, -] - -def ag_grid_simple_themes(): - return rx.vstack( - rx.hstack( - rx.text("Theme:"), - rx.select(AGGridThemeState.themes, value=AGGridThemeState.theme, on_change=AGGridThemeState.set_theme), - ), - rxe.ag_grid( - id="ag_grid_basic_themes", - row_data=df.to_dict("records"), - column_defs=column_defs, - theme=AGGridThemeState.theme, - width="100%", - height="40vh", - ), - width="100%", - ) -``` - -📊 **Dataset source:** [gapminder2007.csv](https://raw.githubusercontent.com/plotly/datasets/master/gapminder2007.csv) \ No newline at end of file diff --git a/docs/enterprise/ag_grid/value-transformers.md b/docs/enterprise/ag_grid/value-transformers.md deleted file mode 100644 index c9de70ebb..000000000 --- a/docs/enterprise/ag_grid/value-transformers.md +++ /dev/null @@ -1,76 +0,0 @@ ---- -order: 2 ---- - -```python exec -import reflex as rx -import reflex_enterprise as rxe -from pcweb.pages.docs import enterprise -``` - -# Value Transformers - -AgGrid allow you to apply transformers based on the column of your grid. This allow you to perform operations on the data before displaying it on the grid, without having to pre-process the data on the backend, reducing the load on your application. - -TOC: -- [Value Getter](#value-getter) -- [Value Formatter](#value-formatter) - -## Value Getter - -`value_getter` is a property of the column definition that allows you to define a function that will be called to get the value of the cell. This function will receive the row data as a parameter and should return the value to be displayed on the cell. - -If you have two columns `col_a` and `col_b` and you want to display the sum of these two columns in a third column `sum`, you can define the `value_getter` of `sum` as follows: - -```python demo exec -import reflex as rx -import reflex_enterprise as rxe -import pandas as pd - - -df = pd.DataFrame({"col_a": [1, 2, 3, 4, 5], "col_b": [10, 20, 30, 40, 50]}) - -column_defs = [ - {"field": "col_a", "header_name": "Column A"}, - {"field": "col_b", "header_name": "Column B"}, - {"field": "sum", "header_name": "Sum", "value_getter": "params.data.col_a + params.data.col_b"}, - rxe.ag_grid.column_def(field="diff", header_name="Difference", value_getter="params.data.col_b - params.data.col_a"), -] - -def ag_grid_value_getter(): - return rxe.ag_grid( - id="ag_grid_value_getter", - row_data=df.to_dict("records"), - column_defs=column_defs, - width="100%", - ) -``` - -## Value Formatter - -`value_formatter` is a property of the column definition that allows you to define a function that will be called to format the value of the cell. This function will receive the value of the cell as a parameter and should return the formatted value to be displayed on the cell. - -If you have a column `price` and you want to display the price with a currency symbol, you can define the `value_formatter` of `price` as follows: - -```python demo exec -import reflex as rx -import reflex_enterprise as rxe -import pandas as pd - -df = pd.DataFrame({"product_name":["Product A", "Product B", "Product C", "Product D", "Product E"], "price": [100, 200, 300, 400, 500]}) -column_defs = [ - {"field": "product_name", "header_name": "Product Name"}, - {"field": "price", "header_name": "Price ($)", "value_formatter": "'$' + params.value"}, - rxe.ag_grid.column_def(col_id="price_eur", header_name="Price (€)", value_formatter="params.data.price + ' €'"), -] - -def ag_grid_value_formatter(): - return rxe.ag_grid( - id="ag_grid_value_formatter", - row_data=df.to_dict("records"), - column_defs=column_defs, - width="100%", - ) -``` - - diff --git a/docs/enterprise/built-with-reflex.md b/docs/enterprise/built-with-reflex.md deleted file mode 100644 index 8fb0f60b9..000000000 --- a/docs/enterprise/built-with-reflex.md +++ /dev/null @@ -1,17 +0,0 @@ -# Built with Reflex Badge - -The "Built with Reflex" badge appears in the bottom right corner of apps using reflex-enterprise components. - -## Removing the Badge - -To remove the badge, you need to be on the Enterprise tier. - -## Configuration - -```python -import reflex_enterprise as rxe - -config = rxe.Config( - show_built_with_reflex=False, # Requires paid tier -) -``` \ No newline at end of file diff --git a/docs/enterprise/components.md b/docs/enterprise/components.md deleted file mode 100644 index 3e82efae4..000000000 --- a/docs/enterprise/components.md +++ /dev/null @@ -1,119 +0,0 @@ ---- -title: Enterprise Components ---- - -```python exec -import reflex as rx -from pcweb.pages.docs import enterprise -from pcweb.templates.docpage import h1_comp, text_comp_2 -from pcweb.components.icons import get_icon - -def enterprise_component_grid(): - sections = [ - { - "title": "AG Grid", - "description": "Advanced data grid with sorting, filtering, editing, and pagination", - "link": enterprise.ag_grid.index.path, - "components": [ - ("Overview", enterprise.ag_grid.index.path), - ("Column Definitions", enterprise.ag_grid.column_defs.path), - ("Aligned Grids", enterprise.ag_grid.aligned_grids.path), - ("Model Wrapper", enterprise.ag_grid.model_wrapper.path), - ("Pivot Mode", enterprise.ag_grid.pivot_mode.path), - ("Theme", enterprise.ag_grid.theme.path), - ("Value Transformers", enterprise.ag_grid.value_transformers.path), - ] - }, - { - "title": "AG Chart", - "description": "Interactive charts and data visualization", - "link": enterprise.ag_chart.path, - "components": [ - ("Overview", enterprise.ag_chart.path), - ] - }, - { - "title": "Interactive Components", - "description": "Drag-and-drop and mapping functionality", - "link": enterprise.drag_and_drop.path, - "components": [ - ("Drag and Drop", enterprise.drag_and_drop.path), - ("Mapping", enterprise.map.index.path), - ] - }, - { - "title": "Mantine", - "description": "Rich UI components from Mantine library", - "link": enterprise.mantine.index.path, - "components": [ - ("Overview", enterprise.mantine.index.path), - ("Autocomplete", enterprise.mantine.autocomplete.path), - ("Collapse", enterprise.mantine.collapse.path), - ("Combobox", enterprise.mantine.combobox.path), - ("JSON Input", enterprise.mantine.json_input.path), - ("Loading Overlay", enterprise.mantine.loading_overlay.path), - ("Multi Select", enterprise.mantine.multi_select.path), - ("Number Formatter", enterprise.mantine.number_formatter.path), - ("Pill", enterprise.mantine.pill.path), - ("Ring Progress", enterprise.mantine.ring_progress.path), - ("Semi Circle Progress", enterprise.mantine.semi_circle_progress.path), - ("Spoiler", enterprise.mantine.spoiler.path), - ("Tags Input", enterprise.mantine.tags_input.path), - ("Timeline", enterprise.mantine.timeline.path), - ("Tree", enterprise.mantine.tree.path), - ] - } - ] - - cards = [] - for section in sections: - cards.append( - rx.box( - rx.link( - rx.el.h1( - section["title"], - class_name="font-large text-slate-12", - ), - get_icon("new_tab", class_name="text-slate-11 [&>svg]:size-4"), - href=section["link"], - underline="none", - class_name="px-4 py-2 bg-slate-1 hover:bg-slate-3 transition-bg flex flex-row justify-between items-center !text-slate-12", - ), - rx.text( - section["description"], - class_name="px-4 py-2 font-small text-slate-9 border-t border-slate-5", - ), - rx.box( - *[ - rx.link( - comp[0], - href=comp[1], - class_name="font-small text-slate-11 hover:!text-violet-9 transition-color w-fit", - ) - for comp in section["components"] - ], - class_name="flex flex-col gap-2.5 px-4 py-2 border-t border-slate-5", - ), - class_name="flex flex-col border border-slate-5 rounded-xl bg-slate-2 shadow-large overflow-hidden", - ) - ) - - return rx.box( - *cards, - class_name="grid grid-cols-1 lg:grid-cols-2 gap-6", - ) - -component_grid = enterprise_component_grid() -``` - -```python eval -h1_comp(text="Enterprise Components") -``` - -```python eval -text_comp_2(text="Advanced UI components and features to enhance your Reflex applications. Available for free with the 'Built with Reflex' badge, or without the badge with an enterprise license.") -``` - -```python eval -component_grid -``` \ No newline at end of file diff --git a/docs/enterprise/drag-and-drop.md b/docs/enterprise/drag-and-drop.md deleted file mode 100644 index ee76470e6..000000000 --- a/docs/enterprise/drag-and-drop.md +++ /dev/null @@ -1,584 +0,0 @@ ---- -title: Drag and Drop ---- - -```python exec -import reflex as rx -import reflex_enterprise as rxe -from pcweb.pages.docs import enterprise -``` - -# Drag and Drop - -Reflex Enterprise provides comprehensive drag and drop functionality for creating interactive UI elements using the `rxe.dnd` module. Built on top of react-dnd, it offers both high-level components for common use cases and low-level hooks for advanced scenarios. - -```md alert warning -# Important: Always decorate functions defining `rxe.dnd.draggable` components with `@rx.memo` to avoid compilation errors. -``` - -## Basic Usage - -### Simple Drag and Drop - -Here's a basic example showing how to create a draggable item and drop target: - -```python demo exec -import reflex as rx -import reflex_enterprise as rxe - -class BasicDndState(rx.State): - drop_count: int = 0 - - def increment_drop_count(self): - self.drop_count += 1 - - -@rx.memo -def draggable_card(): - return rxe.dnd.draggable( - rx.card( - rx.text("Drag me!", weight="bold"), - rx.text("I can be moved around"), - bg="blue.500", - color="white", - p=4, - cursor="grab", - width="200px", - height="100px" - ), - type="BasicCard", - item={"message": "Hello from draggable!"}, - ) - -def basic_drag_drop(): - return rx.vstack( - rx.text(f"Items dropped: {BasicDndState.drop_count}"), - rx.hstack( - draggable_card(), - rxe.dnd.drop_target( - rx.box( - "Drop Zone", - bg="gray.100", - border="2px dashed gray", - min_height="150px", - min_width="200px", - display="flex", - align_items="center", - justify_content="center", - font_weight="bold" - ), - accept=["BasicCard"], - on_drop=BasicDndState.increment_drop_count, - ), - spacing="4", - align="start" - ), - spacing="4" - ) -``` - -### Multi-Position Drag and Drop - -Create a draggable item that can be moved between multiple drop targets: - -```python demo exec -import reflex as rx -import reflex_enterprise as rxe - -class MultiPositionState(rx.State): - card_position: int = 0 - - def set_card_position(self, position: int): - self.card_position = position - - -@rx.memo -def movable_card(): - return rxe.dnd.draggable( - rx.card( - rx.text("Movable Card", weight="bold"), - rx.text("Position: " + MultiPositionState.card_position.to_string()), - bg="purple.500", - color="white", - p=4, - width="180px", - height="120px" - ), - type="MovableCard", - border="2px solid purple", - ) - -def drop_zone(position: int): - params = rxe.dnd.DropTarget.collected_params - return rxe.dnd.drop_target( - rx.cond( - MultiPositionState.card_position == position, - movable_card(), - rx.box( - f"Drop Zone {position}", - color="gray.600", - font_weight="bold" - ) - ), - width="200px", - height="200px", - border="2px solid red", - border_color=rx.cond(params.is_over, "green.500", "red.500"), - bg=rx.cond(params.is_over, "green.100", "blue.100"), - accept=["MovableCard"], - on_drop=lambda _: MultiPositionState.set_card_position(position), - display="flex", - align_items="center", - justify_content="center" - ) - -def multi_position_example(): - return rx.vstack( - rx.text("Drag the card between positions", weight="bold"), - rx.grid( - drop_zone(0), - drop_zone(1), - drop_zone(2), - drop_zone(3), - columns="2", - spacing="4" - ), - spacing="4" - ) -``` - -## Advanced Features - -### State Tracking with Collected Parameters - -Access drag and drop state information using collected parameters: - -```python demo exec -import reflex as rx -import reflex_enterprise as rxe - -class StateTrackingState(rx.State): - drag_info: str = "No drag activity" - - def set_drag_info(self, value: str): - self.drag_info = value - -@rx.memo -def tracked_draggable(): - drag_params = rxe.dnd.Draggable.collected_params - return rxe.dnd.draggable( - rx.card( - rx.text("Tracked Draggable"), - rx.text(rx.cond(drag_params.is_dragging, "Dragging...", "Ready to drag")), - bg=rx.cond(drag_params.is_dragging, "orange.500", "blue.500"), - color="white", - p=4, - opacity=rx.cond(drag_params.is_dragging, 0.5, 1.0) - ), - type="TrackedItem", - on_end=StateTrackingState.set_drag_info("Drag ended") - ) - -def tracked_drop_target(): - drop_params = rxe.dnd.DropTarget.collected_params - return rxe.dnd.drop_target( - rx.box( - rx.text("Smart Drop Zone"), - rx.text(rx.cond(drop_params.is_over, "Ready to receive!", "Waiting...")), - bg=rx.cond(drop_params.is_over, "green.200", "gray.100"), - border=rx.cond(drop_params.is_over, "2px solid green", "2px dashed gray"), - p=4, - min_height="150px", - display="flex", - flex_direction="column", - align_items="center", - justify_content="center" - ), - accept=["TrackedItem"], - on_drop=StateTrackingState.set_drag_info("Item successfully dropped!"), - on_hover=StateTrackingState.set_drag_info("Item hovering over drop zone") - ) - -def state_tracking_example(): - return rx.vstack( - rx.text(f"Status: {StateTrackingState.drag_info}"), - rx.hstack( - tracked_draggable(), - tracked_drop_target(), - spacing="4" - ), - spacing="4" - ) -``` - -### Dynamic Lists with Drag and Drop - -Create dynamic draggable lists using `rx.foreach`: - -```python demo exec -import dataclasses -import reflex as rx -import reflex_enterprise as rxe - -@dataclasses.dataclass -class ListItem: - id: str - text: str - list_id: str - -class DynamicListState(rx.State): - list_a: list[ListItem] = [ - ListItem(id="1", text="Item 1", list_id="A"), - ListItem(id="2", text="Item 2", list_id="A"), - ListItem(id="3", text="Item 3", list_id="A"), - ] - list_b: list[ListItem] = [ - ListItem(id="4", text="Item 4", list_id="B"), - ListItem(id="5", text="Item 5", list_id="B"), - ] - - def move_item(self, item_data: dict, target_list: str): - item_id = item_data.get("id") - source_list = item_data.get("list_id") - - if not item_id or not source_list: - return - - # Find the item in the source list - source_items = getattr(self, f"list_{source_list.lower()}") - item_to_move = None - for item in source_items: - if item.id == item_id: - item_to_move = item - break - - if not item_to_move: - return - - # Remove from source list only - if source_list == "A": - self.list_a = [item for item in self.list_a if item.id != item_id] - else: - self.list_b = [item for item in self.list_b if item.id != item_id] - - # Create new item for target list - new_item = ListItem( - id=item_id, - text=item_to_move.text, - list_id=target_list - ) - - # Add to target list - if target_list == "A": - self.list_a.append(new_item) - else: - self.list_b.append(new_item) - -@rx.memo -def draggable_list_item(item: ListItem): - return rxe.dnd.draggable( - rx.card( - rx.text(item.text, weight="bold"), - rx.text(f"From List {item.list_id}", size="2", color="gray.600"), - p=3, - cursor="grab", - _hover={"bg": "gray.50"} - ), - type="ListItem", - item={"id": item.id, "text": item.text, "list_id": item.list_id}, - ) - -def droppable_list(title: str, items: list[ListItem], list_id: str): - return rxe.dnd.drop_target( - rx.vstack( - rx.text(title, weight="bold", size="5"), - rx.vstack( - rx.foreach(items, lambda item, index: draggable_list_item(item=item)), - spacing="2", - min_height="200px", - width="100%" - ), - bg="gray.50", - p=4, - border_radius="md", - border="2px dashed gray", - width="250px" - ), - accept=["ListItem"], - on_drop=lambda item: DynamicListState.move_item(item, list_id) - ) - -def dynamic_list_example(): - return rx.hstack( - droppable_list("List A", DynamicListState.list_a, "A"), - droppable_list("List B", DynamicListState.list_b, "B"), - spacing="6", - align="start" - ) -``` - -## Core Components - -### Draggable - -The `rxe.dnd.draggable` component makes any element draggable: - -**Key Properties:** -- `type`: String identifier for drag type matching -- `item`: Data object passed to drop handlers -- `on_end`: Called when drag operation ends - -### Drop Target - -The `rxe.dnd.drop_target` component creates areas that accept draggable items: - -**Key Properties:** -- `accept`: List of drag types this target accepts -- `on_drop`: Called when item is dropped -- `on_hover`: Called when item hovers over target - -### Collected Parameters - -Access real-time drag/drop state: - -**Draggable Parameters (`rxe.dnd.Draggable.collected_params`):** -- `is_dragging`: Boolean indicating if item is being dragged - -**Drop Target Parameters (`rxe.dnd.DropTarget.collected_params`):** -- `is_over`: Boolean indicating if draggable is hovering -- `can_drop`: Boolean indicating if drop is allowed - -# API Reference - -### rxe.dnd.draggable - -Creates a draggable component that can be moved around the interface. - -**Parameters:** - -- **`type`** (str, required): String identifier that must match the `accept` list of drop targets -- **`item`** (dict | Callable): Data object passed to drop handlers. Can be a static dictionary or a function that receives a `DragSourceMonitor` and returns data -- **`preview_options`** (dict): Configuration for the drag preview appearance -- **`options`** (dict): Additional drag source options like `dropEffect` -- **`on_end`** (EventHandler): Event handler called when drag operation completes -- **`can_drag`** (Callable): Function that determines if the item can be dragged -- **`is_dragging`** (Callable): Function to override the default dragging state detection -- **`collect`** (Callable): Function to collect custom properties from the drag monitor - -### rxe.dnd.drop_target - -Creates a drop target that can receive draggable items. - -**Parameters:** - -- **`accept`** (str | list[str], required): Drag type(s) this target accepts -- **`options`** (dict): Additional drop target configuration options -- **`on_drop`** (EventHandler): Event handler called when an item is dropped, receives the `item` data -- **`on_hover`** (EventHandler): Event handler called when an item hovers over the target -- **`can_drop`** (Callable): Function that determines if a specific item can be dropped -- **`collect`** (Callable): Function to collect custom properties from the drop monitor - -## Monitor Classes - -### DragSourceMonitor - -Provides information about the drag operation state: - -- **`is_dragging()`**: Returns `True` if this item is currently being dragged -- **`can_drag()`**: Returns `True` if the item can be dragged -- **`get_item()`**: Returns the item data being dragged -- **`get_item_type()`**: Returns the drag type string -- **`get_drop_result()`**: Returns the drop result (available in `on_end`) -- **`did_drop()`**: Returns `True` if the item was successfully dropped - -### DropTargetMonitor - -Provides information about the drop target state: - -- **`is_over()`**: Returns `True` if a draggable item is hovering over this target -- **`can_drop()`**: Returns `True` if the hovering item can be dropped -- **`get_item()`**: Returns the item data of the hovering draggable -- **`get_item_type()`**: Returns the drag type of the hovering item - -## Default Collected Parameters - -### Draggable.collected_params - -```python -{ - "is_dragging": bool, # True when this item is being dragged - "can_drag": bool # True when this item can be dragged -} -``` - -### DropTarget.collected_params - -```python -{ - "is_over": bool, # True when a draggable is hovering - "can_drop": bool, # True when the hovering item can be dropped - "item": dict | None # Data from the hovering draggable item -} -``` - -## Advanced Usage Examples - -### Data Passing with Item Parameter - -The `item` parameter allows you to pass data from draggable components to drop handlers: - -```python demo exec toggle -import reflex as rx -import reflex_enterprise as rxe - -class SimpleState(rx.State): - message: str = "No items dropped yet" - - def set_message_from_item(self, item: dict): - self.message = f"Dropped: {item['name']}" - -def simple_draggable(): - return rxe.dnd.draggable( - rx.box( - "Drag me!", - p=4, - bg="blue.100", - border="1px solid blue", - cursor="grab" - ), - type="simple", - item={"name": "test_item", "value": 42} - ) - -def simple_drop_target(): - return rxe.dnd.drop_target( - rx.box( - rx.text(SimpleState.message), - p=4, - bg="gray.100", - border="2px dashed gray", - min_height="100px" - ), - accept=["simple"], - on_drop=SimpleState.set_message_from_item - ) - -def item_data_example(): - return rx.vstack( - simple_draggable(), - simple_drop_target(), - spacing="4" - ) -``` - -### Custom Collect Functions - -The `collect` parameter allows you to access drag and drop state information in real-time: - -```python demo exec toggle -import reflex as rx -import reflex_enterprise as rxe - -class CollectState(rx.State): - drag_info: str = "No drag activity" - drop_info: str = "No drop activity" - - def handle_drop(self, item: dict): - self.drop_info = f"Dropped: {item.get('name', 'Unknown')}" - return rx.toast(f"Successfully dropped {item.get('name', 'item')}") - - - -def collect_draggable(): - params = rxe.dnd.Draggable.collected_params - return rxe.dnd.draggable( - rx.box( - rx.vstack( - rx.text("Drag me!", weight="bold"), - rx.text(f"Dragging: {params.is_dragging}", size="2"), - rx.text(f"Can drag: {params.can_drag}", size="2"), - spacing="1" - ), - p=4, - bg=rx.cond(params.is_dragging, "blue.200", "blue.100"), - border="1px solid blue", - cursor=rx.cond(params.is_dragging, "grabbing", "grab"), - opacity=rx.cond(params.is_dragging, 0.7, 1.0) - ), - type="collect_item", - item={"id": "collect_test", "name": "Test Item"} - ) - -def collect_drop_target(): - params = rxe.dnd.DropTarget.collected_params - return rxe.dnd.drop_target( - rx.box( - rx.vstack( - rx.text("Drop Zone", weight="bold"), - rx.text(f"Is over: {params.is_over}", size="2"), - rx.text(f"Can drop: {params.can_drop}", size="2"), - rx.cond( - params.item, - rx.text(f"Hovering item: {params.item.get('name', 'Unknown')}", size="2"), - rx.text("No item hovering", size="2") - ), - spacing="1" - ), - p=4, - bg=rx.cond( - params.is_over & params.can_drop, - "green.200", - rx.cond(params.is_over, "yellow.200", "gray.100") - ), - border=rx.cond( - params.is_over & params.can_drop, - "2px solid green", - rx.cond(params.is_over, "2px solid yellow", "2px dashed gray") - ), - min_height="120px" - ), - accept=["collect_item"], - on_drop=CollectState.handle_drop, - ) - -def custom_collect_example(): - return rx.vstack( - rx.text("Real-time Monitor State", weight="bold", size="4"), - rx.hstack( - collect_draggable(), - collect_drop_target(), - spacing="6", - align="start" - ), - rx.text(CollectState.drop_info, size="2", color="gray.600"), - spacing="4" - ) -``` - -## Provider - -Drag and drop functionality requires the `rxe.dnd.provider` component to wrap your app. The provider is automatically added when using `draggable` or `drop_target` components. - -For manual control: - -```python -def app(): - return rxe.dnd.provider( - # Your app content - your_app_content(), - backend="HTML5" # or "Touch" for mobile - ) -``` - -## Best Practices - -1. **Always use `@rx.memo`** on functions containing draggable components -2. **Use descriptive type names** for better debugging -3. **Handle edge cases** in drop handlers (invalid items, etc.) -4. **Provide visual feedback** using collected parameters -5. **Test on mobile devices** with touch backend -6. **Keep item data lightweight** for better performance - ---- - -[← Back to main documentation]({enterprise.overview.path}) diff --git a/docs/enterprise/mantine/autocomplete.md b/docs/enterprise/mantine/autocomplete.md deleted file mode 100644 index 6a9f826c3..000000000 --- a/docs/enterprise/mantine/autocomplete.md +++ /dev/null @@ -1,22 +0,0 @@ ---- -title: Autocomplete ---- - -# Autocomplete component - -`rxe.mantine.autocomplete` is a component for providing suggestions as the user types. It is useful for enhancing user experience by offering relevant options based on input. - -```python demo exec -import reflex as rx -import reflex_enterprise as rxe - -def autocomplete_example(): - return rx.vstack( - rxe.mantine.autocomplete( - data=["Apple", "Banana", "Cherry", "Date", "Elderberry"], - placeholder="Type a fruit", - label="Fruit Autocomplete", - description="Select a fruit from the list", - ), - ) -``` diff --git a/docs/enterprise/mantine/collapse.md b/docs/enterprise/mantine/collapse.md deleted file mode 100644 index 7ed250261..000000000 --- a/docs/enterprise/mantine/collapse.md +++ /dev/null @@ -1,36 +0,0 @@ ---- -title: Collapse ---- - -# Collapse component - -`rxe.mantine.collapse` is a component that allows you to create collapsible sections in your application. It is useful for hiding or showing content based on user interaction, such as clicking a button or a link. - -```python demo exec -import reflex as rx -import reflex_enterprise as rxe - -class CollapseState(rx.State): - is_open: bool = False - - @rx.event - def toggle_collapse(self): - self.is_open = not self.is_open - -def collapse_example(): - return rx.vstack( - rxe.mantine.collapse( - rx.text( - "This is a collapsible section. Click the button to toggle the collapse.", - font_size="lg", - ), - in_=CollapseState.is_open, - label="Collapsible Section", - description="Click the button to toggle the collapse.", - ), - rx.button( - "Toggle Collapse", - on_click=lambda: CollapseState.toggle_collapse, - ), - ) -``` diff --git a/docs/enterprise/mantine/combobox.md b/docs/enterprise/mantine/combobox.md deleted file mode 100644 index d212a9f1c..000000000 --- a/docs/enterprise/mantine/combobox.md +++ /dev/null @@ -1,36 +0,0 @@ ---- -title: Combobox ---- - -```python exec -import reflex as rx -import reflex_enterprise as rxe -from pcweb.pages.docs import enterprise -``` - -# Combobox - -`rxe.mantine.combobox` is a wrapping of the mantine component [Combobox](https://mantine.dev/core/combobox/). It is a simple component that can be used to display a list of options, and allows the user to select one or more options from the list. It can be used in various contexts, such as in a form or as a standalone component. - -```python -import reflex as rx -import reflex_enterprise as rxe - -def combobox_page(): - """Combobox demo.""" - return rxe.mantine.combobox( - rxe.mantine.combobox.target( - rx.input(type="button"), - ), - rxe.mantine.combobox.dropdown( - rxe.mantine.combobox.options( - rxe.mantine.combobox.option("Option 1"), - rxe.mantine.combobox.option("Option 2"), - rxe.mantine.combobox.option("Option 3"), - ), - ), - label="Combobox", - placeholder="Select a value", - ) -``` - diff --git a/docs/enterprise/mantine/index.md b/docs/enterprise/mantine/index.md deleted file mode 100644 index 58d48822d..000000000 --- a/docs/enterprise/mantine/index.md +++ /dev/null @@ -1,26 +0,0 @@ ---- -title: Mantine -order: 4 ---- - -# Mantine - -Mantine is a React component library that provides a set of high-quality components and hooks for building modern web applications. It is designed to be flexible, customizable, and easy to use, making it a popular choice among developers. - -Some of those components have been integrated into Reflex Enterprise, allowing you to use them in your Reflex applications. The following components are available: -- JsonInput -- Autocomplete -- ComboBox -- Multiselect -- Pill -- PillsInput -- TagsInput -- Tree -- RingProgress -- SemiCircleProgress -- LoadingOverlay -- NumberFormatter -- Spoiler -- Timeline -- Collapse - diff --git a/docs/enterprise/mantine/json-input.md b/docs/enterprise/mantine/json-input.md deleted file mode 100644 index 573773237..000000000 --- a/docs/enterprise/mantine/json-input.md +++ /dev/null @@ -1,35 +0,0 @@ ---- -title: JSON Input ---- - -# JSON Input - -`rxe.mantine.json_input` is a component that allows you to input JSON data in a user-friendly way. It provides validation and formatting features to ensure that the JSON data is correctly structured. - -## Example - -```python demo exec -import reflex as rx -import reflex_enterprise as rxe - -class JsonInputState(rx.State): - json_data: str = "" - - def set_json_data(self, value: str): - self.json_data = value - - -def json_input_example(): - return rxe.mantine.json_input( - id="json-input", - value=JsonInputState.json_data, - placeholder="Enter JSON data", - label="JSON Input", - description="Please enter valid JSON data.", - required=True, - size="md", - format_on_blur=True, - on_change=JsonInputState.set_json_data, - width="300px", - ) -``` diff --git a/docs/enterprise/mantine/loading-overlay.md b/docs/enterprise/mantine/loading-overlay.md deleted file mode 100644 index 8aebe7cdd..000000000 --- a/docs/enterprise/mantine/loading-overlay.md +++ /dev/null @@ -1,32 +0,0 @@ ---- -title: Loading Overlay ---- - -# Loading Overlay component -`rxe.mantine.loading_overlay` is a component that displays a loading overlay on top of its children. It is useful for indicating that a process is ongoing and prevents user interaction with the underlying content. - -```python demo exec -import reflex as rx -import reflex_enterprise as rxe - -class LoadingOverlayState(rx.State): - loading: bool = False - - @rx.event - def toggle_loading(self): - self.loading = not self.loading - -def loading_overlay_example(): - return rx.container( - rxe.mantine.loading_overlay( - rx.text( - "Loading Overlay Example", - height="200px", - width="100px", - ), - overlay_props={"radius": "sm", "blur": 2}, - visible=LoadingOverlayState.loading, - z_index=1000, - ), - ),rx.button("Toggle Loading", on_click=LoadingOverlayState.toggle_loading), -``` diff --git a/docs/enterprise/mantine/multi-select.md b/docs/enterprise/mantine/multi-select.md deleted file mode 100644 index 5137409be..000000000 --- a/docs/enterprise/mantine/multi-select.md +++ /dev/null @@ -1,31 +0,0 @@ ---- -title: MultiSelect ---- - -# MultiSelect component - -`rxe.mantine.multi_select` is a component for selecting multiple options from a list. It allows users to choose one or more items, making it suitable for scenarios where multiple selections are required. - -```python demo exec -import reflex as rx -import reflex_enterprise as rxe - -class MultiSelectState(rx.State): - selected_fruits: list = [] - - def set_selected_fruits(self, value: list): - self.selected_fruits = value - - -def multi_select_example(): - return rx.vstack( - rxe.mantine.multi_select( - label="Select fruits", - placeholder="Pick all that you like", - data=["Apple", "Banana", "Cherry", "Date", "Elderberry"], - value=MultiSelectState.selected_fruits, - on_change=MultiSelectState.set_selected_fruits, - ) - ) - -``` diff --git a/docs/enterprise/mantine/number-formatter.md b/docs/enterprise/mantine/number-formatter.md deleted file mode 100644 index d2833125d..000000000 --- a/docs/enterprise/mantine/number-formatter.md +++ /dev/null @@ -1,27 +0,0 @@ ---- -title: Number Formatter ---- - -# Number Formatter component -`rxe.mantine.number_formatter` is a component for formatting numbers in a user-friendly way. It allows you to specify the format, precision, and other options for displaying numbers. - -```python demo exec -import reflex as rx -import reflex_enterprise as rxe - -def number_formatter_example(): - return rx.vstack( - rxe.mantine.number_formatter( - value=100, - prefix="$", - ), - rxe.mantine.number_formatter( - value=100, - suffix="€", - ), - rxe.mantine.number_formatter( - value=1234567.89, - thousand_separator=True, - ), - ) -``` diff --git a/docs/enterprise/mantine/pill.md b/docs/enterprise/mantine/pill.md deleted file mode 100644 index e401685eb..000000000 --- a/docs/enterprise/mantine/pill.md +++ /dev/null @@ -1,94 +0,0 @@ ---- -title: Pill ---- - -```python exec -import reflex as rx -import reflex_enterprise as rxe -from pcweb.pages.docs import enterprise -``` - -# Pill - -`rxe.mantine.pill` is a wrapping of the mantine component [Pill](https://mantine.dev/core/pill/). It is a simple component that can be used to display a small piece of information, such as a tag or a label. It can be used in various contexts, such as in a list of tags or labels, or as a standalone component. - -```python demo exec -import reflex as rx -import reflex_enterprise as rxe - -def pill_page(): - """Pill demo.""" - return rxe.mantine.pill( - "Pill", - color="blue", - size="md", - variant="outline", - radius="xl", - with_remove_button=True, - on_remove=lambda: rx.toast("Pill on_remove triggered"), - ) -``` - -## Pill Group -`rxe.mantine.pill.group` allows grouping multiple `rxe.mantine.pill` components together, with a predefined layout. - -```python demo exec -import reflex as rx -import reflex_enterprise as rxe - -def pill_group_page(): - """Pill demo.""" - return rxe.mantine.pill.group( - rxe.mantine.pill("Pill 1"), - rxe.mantine.pill("Pill 2"), - ) -``` - - -# PillsInput - -`rxe.mantine.pills_input` is a wrapping of the mantine component [PillsInput](https://mantine.dev/core/pills-input/). It is an utility component that can be used to display a list of tags or labels. It can be used in various contexts, such as in a form or as a standalone component. -By itself it does not include any logic, it only renders given children. - -```md alert info -# For a fully functional out-of-the-box component, consider using [`rxe.mantine.tags_input`](/docs/enterprise/mantine/tags-input/) instead. -``` - -## Example - -```python demo exec -import reflex as rx -import reflex_enterprise as rxe - -class PillInputState(rx.State): - """State for the PillsInput demo.""" - - tags: set[str] = {"Foo", "Bar"} - - @rx.event - def add_tag(self, tag: str): - """Add a tag to the list of tags.""" - self.tags.add(tag) - - @rx.event - def remove_tag(self, tag: str): - """Remove a tag from the list of tags.""" - self.tags.remove(tag) - -def pills_input_page(): - """PillsInput demo.""" - return rxe.mantine.pills_input( - rxe.mantine.pill.group( - rx.foreach( - PillInputState.tags, lambda tag: rxe.mantine.pill(tag, with_remove_button=True, on_remove=PillInputState.remove_tag(tag)) - ), - rxe.mantine.pills_input.field( - placeholder="Enter tags", - # on_blur=PillInputState.add_tag, - ), - ), - label="PillsInput", - id="pills-input", - value=["tag1", "tag2"], - ) -``` diff --git a/docs/enterprise/mantine/ring-progress.md b/docs/enterprise/mantine/ring-progress.md deleted file mode 100644 index 9612531d2..000000000 --- a/docs/enterprise/mantine/ring-progress.md +++ /dev/null @@ -1,31 +0,0 @@ ---- -title: Ring Progress ---- - -# Ring Progress component - -`rxe.mantine.ring_progress` is a component for displaying progress in a circular format. It is useful for visualizing completion percentages or other metrics in a compact and visually appealing way. - -```python demo exec -import reflex as rx -import reflex_enterprise as rxe -import random - -class RingProgressState(rx.State): - value: int = 50 - - @rx.event - def random(self): - self.value = random.randint(0, 100) - -def ring_progress_example(): - return rx.vstack( - rxe.mantine.ring_progress( - size=100, - sections=[ - {"value": RingProgressState.value, "color": "blue"}, - ], - ), - rx.button("Randomize", on_click=RingProgressState.random), - ) -``` diff --git a/docs/enterprise/mantine/semi-circle-progress.md b/docs/enterprise/mantine/semi-circle-progress.md deleted file mode 100644 index 3f7f1f5df..000000000 --- a/docs/enterprise/mantine/semi-circle-progress.md +++ /dev/null @@ -1,28 +0,0 @@ ---- -title: Semi Circle Progress ---- - -# Semi Circle Progress component -`rxe.mantine.semi_circle_progress` is a component for displaying progress in a semi-circular format. It is useful for visualizing completion percentages or other metrics in a compact and visually appealing way. - -```python demo exec -import reflex as rx -import reflex_enterprise as rxe -import random - -class SemiCircleProgressState(rx.State): - value: int = 50 - - @rx.event - def random(self): - self.value = random.randint(0, 100) - -def semi_circle_progress_example(): - return rx.vstack( - rxe.mantine.semi_circle_progress( - size=100, - value=SemiCircleProgressState.value, - ), - rx.button("Randomize", on_click=SemiCircleProgressState.random), - ) -``` diff --git a/docs/enterprise/mantine/spoiler.md b/docs/enterprise/mantine/spoiler.md deleted file mode 100644 index 09a1b0dc1..000000000 --- a/docs/enterprise/mantine/spoiler.md +++ /dev/null @@ -1,22 +0,0 @@ ---- -title: Spoiler ---- - -# Spoiler component - -`rxe.mantine.spoiler` is a component that allows you to hide or reveal content. It is useful for displaying additional information or details that may not be immediately relevant to the user. - -```python demo exec -import reflex as rx -import reflex_enterprise as rxe - -def spoiler_example(): - return rx.vstack( - rxe.mantine.spoiler( - "This is a spoiler zone where lorem ipsum text dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit...", - max_height=50, - show_label="Show more", - hide_label="Show less", - ), - ) -``` diff --git a/docs/enterprise/mantine/tags-input.md b/docs/enterprise/mantine/tags-input.md deleted file mode 100644 index 5b24a1a63..000000000 --- a/docs/enterprise/mantine/tags-input.md +++ /dev/null @@ -1,55 +0,0 @@ ---- -title: TagsInput ---- - -# TagsInput - -`rxe.mantine.tags_input` is a wrapping of the mantine component [TagsInput](https://mantine.dev/core/tags-input/). It is an utility component that can be used to display a list of tags or labels. It can be used in various contexts, such as in a form or as a standalone component. - -```md alert info -# You can use the props mentioned in the Mantine documentation, but they need to be passed in snake_case. -``` - -## Basic Example - -```python demo exec -import reflex as rx -import reflex_enterprise as rxe - -def tags_input_simple_page(): - """TagsInput demo.""" - return rxe.mantine.tags_input( - placeholder="Enter tags", - label="Press Enter to ad a tag", - ) -``` - -## State Example - -```python demo exec -import reflex as rx -import reflex_enterprise as rxe - -class TagsInputState(rx.State): - """State for the TagsInput component.""" - - tags: list[str] = ["Tag1", "Tag2"] - - @rx.event - def update_tags(self, tags: list[str]): - """Add a tag to the list of tags.""" - self.tags = tags - -def tags_input_page(): - """TagsInput demo.""" - return rxe.mantine.tags_input( - value=TagsInputState.tags, - on_change=TagsInputState.update_tags, - placeholder="Enter tags", - label="TagsInput", - description="This is a TagsInput component", - error="", - size="md", - radius="xl", - ) -``` diff --git a/docs/enterprise/mantine/timeline.md b/docs/enterprise/mantine/timeline.md deleted file mode 100644 index 32843a35b..000000000 --- a/docs/enterprise/mantine/timeline.md +++ /dev/null @@ -1,34 +0,0 @@ ---- -title: Timeline ---- - -# Timeline component -`rxe.mantine.timeline` is a component for displaying a sequence of events or milestones in a linear format. It is useful for visualizing progress, history, or any sequential information. - -```python demo exec -import reflex as rx -import reflex_enterprise as rxe - -def timeline_example(): - return rx.vstack( - rxe.mantine.timeline( - rxe.mantine.timeline.item( - title="Step 1", - bullet="•", - ), - rxe.mantine.timeline.item( - title="Step 2", - bullet="•", - ), - rxe.mantine.timeline.item( - title="Step 3", - bullet="•", - ), - active=1, - bullet_size=24, - line_width=2, - color="blue", - - ) - ) -``` diff --git a/docs/enterprise/mantine/tree.md b/docs/enterprise/mantine/tree.md deleted file mode 100644 index a3ce63edd..000000000 --- a/docs/enterprise/mantine/tree.md +++ /dev/null @@ -1,31 +0,0 @@ ---- -title: Tree ---- - -# Tree component - -`rxe.mantine.tree` is a component for displaying hierarchical data in a tree structure. It allows users to expand and collapse nodes, making it easy to navigate through large datasets. - -```md alert warning -# Due to some technical limitations(pydantic), the tree component only supports 5 levels of depths for the `data` props. -``` - -```python demo exec -import reflex as rx -import reflex_enterprise as rxe - -def tree_example(): - # return rx.text("Placeholder for tree example") - return rxe.mantine.tree( - data=[ - { - "value": "0", - "label": "Root", - "children": [ - {"value": "0-1", "label": "Child 1"}, - {"value": "0-2", "label": "Child 2"}, - ], - } - ], - ) -``` diff --git a/docs/enterprise/map/index.md b/docs/enterprise/map/index.md deleted file mode 100644 index 20af51880..000000000 --- a/docs/enterprise/map/index.md +++ /dev/null @@ -1,565 +0,0 @@ ---- -title: Interactive Maps ---- - -# Interactive Maps - -```python exec -import reflex as rx -import reflex_enterprise as rxe -from pcweb.pages.docs import enterprise -``` - -The map components in Reflex Enterprise provide interactive mapping capabilities built on top of **Leaflet**, one of the most popular open-source JavaScript mapping libraries. These components enable you to create rich, interactive maps with markers, layers, controls, and event handling. - -```md alert info -# All map components are built using Leaflet and react-leaflet, providing a familiar and powerful mapping experience. -For advanced Leaflet features, refer to the [Leaflet documentation](https://leafletjs.com/reference.html). -``` - -🌍 **[View Live Demo](https://map.reflex.run)** - See the map components in action with interactive examples. - -## Installation & Setup - -Map components are included with `reflex-enterprise`. No additional installation is required. - -## Basic Usage - -Here's a simple example of creating a map with a marker: - -```python demo exec -import reflex as rx -import reflex_enterprise as rxe - -class MapState(rx.State): - center: rxe.map.LatLng = rxe.map.latlng(lat=51.505, lng=-0.09) - zoom: float = 13.0 - -def basic_map(): - return rxe.map( - rxe.map.tile_layer( - url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", - attribution='© OpenStreetMap contributors' - ), - rxe.map.marker( - rxe.map.popup("Hello from London!"), - position=MapState.center, - ), - id="basic-map", - center=MapState.center, - zoom=MapState.zoom, - height="400px", - width="100%", - ) -``` - -## Core Components - -### Map Container - -The `rxe.map()` component is the primary container that holds all other map elements: - -```python -rxe.map( - # Child components (markers, layers, controls) - id="my-map", - center=rxe.map.latlng(lat=51.505, lng=-0.09), - zoom=13, - height="400px", - width="100%" -) -``` - -**Key Properties:** -- `center`: Initial map center coordinates -- `zoom`: Initial zoom level (0-18+ depending on tile provider) -- `bounds`: Alternative to center/zoom, fits map to bounds -- `height`/`width`: Map container dimensions - -### Tile Layers - -Tile layers provide the base map imagery. The most common is OpenStreetMap: - -```python -rxe.map.tile_layer( - url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", - attribution='© OpenStreetMap contributors' -) -``` - - -### Markers - -Add point markers to specific locations: - -```python demo exec -import reflex as rx -import reflex_enterprise as rxe - -def markers_example(): - return rxe.map( - rxe.map.tile_layer( - url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", - attribution='© OpenStreetMap contributors' - ), - rxe.map.marker( - rxe.map.popup( - rx.vstack( - rx.text("Big Ben", weight="bold"), - rx.text("Famous clock tower in London"), - spacing="2" - ) - ), - position=rxe.map.latlng(lat=51.4994, lng=-0.1245), - ), - rxe.map.marker( - rxe.map.popup("London Eye"), - position=rxe.map.latlng(lat=51.5033, lng=-0.1196), - ), - id="markers-map", - center=rxe.map.latlng(lat=51.501, lng=-0.122), - zoom=14, - height="400px", - width="100%", - ) -``` - -### Vector Layers - -Draw shapes and areas on the map: - -```python demo exec -import reflex as rx -import reflex_enterprise as rxe - -def vectors_example(): - return rxe.map( - rxe.map.tile_layer( - url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", - attribution='© OpenStreetMap contributors' - ), - # Circle (radius in meters) - rxe.map.circle( - center=rxe.map.latlng(lat=51.505, lng=-0.09), - radius=500, - path_options=rxe.map.path_options( - color="#ff0000", - fill_color="#ff3333", - fill_opacity=0.3, - weight=2 - ) - ), - # Polygon - rxe.map.polygon( - positions=[ - rxe.map.latlng(lat=51.515, lng=-0.08), - rxe.map.latlng(lat=51.515, lng=-0.07), - rxe.map.latlng(lat=51.520, lng=-0.07), - rxe.map.latlng(lat=51.520, lng=-0.08), - ], - path_options=rxe.map.path_options( - color="#0000ff", - fill_color="#3333ff", - fill_opacity=0.3 - ) - ), - # Polyline - rxe.map.polyline( - positions=[ - rxe.map.latlng(lat=51.500, lng=-0.095), - rxe.map.latlng(lat=51.510, lng=-0.085), - rxe.map.latlng(lat=51.515, lng=-0.095), - ], - path_options=rxe.map.path_options( - color="#00ff00", - weight=4 - ) - ), - id="vectors-map", - center=rxe.map.latlng(lat=51.510, lng=-0.08), - zoom=13, - height="400px", - width="100%", - ) -``` - -## Interactive Features - -### Event Handling - -Maps support comprehensive event handling for user interactions: - -```python demo exec -import reflex as rx -import reflex_enterprise as rxe - -class InteractiveMapState(rx.State): - last_click: str = "No clicks yet" - current_zoom: float = 13.0 - - def handle_map_click(self, event): - lat = event.get("latlng", {}).get("lat", 0) - lng = event.get("latlng", {}).get("lng", 0) - self.last_click = f"Clicked at: {lat:.4f}, {lng:.4f}" - - def handle_zoom_change(self, event): - self.current_zoom = float(event.get("target", {}).get("_zoom", 13.0)) - -def interactive_example(): - return rx.vstack( - rx.text(f"Last click: {InteractiveMapState.last_click}"), - rx.text(f"Current zoom: {InteractiveMapState.current_zoom}"), - rxe.map( - rxe.map.tile_layer( - url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", - attribution='© OpenStreetMap contributors' - ), - id="interactive-map", - center=rxe.map.latlng(lat=51.505, lng=-0.09), - zoom=InteractiveMapState.current_zoom, - height="350px", - width="100%", - on_click=InteractiveMapState.handle_map_click, - on_zoom=InteractiveMapState.handle_zoom_change, - ), - spacing="3" - ) -``` - -### Map Controls - -Add UI controls for enhanced user interaction: - -```python demo exec -import reflex as rx -import reflex_enterprise as rxe - -def controls_example(): - return rxe.map( - rxe.map.tile_layer( - url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", - attribution='© OpenStreetMap contributors' - ), - rxe.map.zoom_control(position="topright"), - rxe.map.scale_control(position="bottomleft"), - rxe.map.attribution_control(position="bottomright"), - id="controls-map", - center=rxe.map.latlng(lat=51.505, lng=-0.09), - zoom=13, - height="400px", - width="100%", - ) -``` - -## Helper Functions - -### Coordinate Creation - -```python -# Create latitude/longitude coordinates -center = rxe.map.latlng(lat=51.505, lng=-0.09, nround=4) - -# Create bounds -bounds = rxe.map.latlng_bounds( - corner1_lat=51.49, corner1_lng=-0.11, - corner2_lat=51.52, corner2_lng=-0.07 -) -``` - -## Map API - -The Map API provides programmatic control over your maps, allowing you to manipulate the map programmatically from your Reflex state methods. - -### Getting the API Reference - -To access the Map API, you need to get a reference to your map using its ID: - -```python -map_api = rxe.map.api("my-map-id") -``` - -### Interactive Demo - -Here are some commonly used API methods demonstrated in action: - -```python demo exec -import reflex as rx -import reflex_enterprise as rxe - -map_api = rxe.map.api("api-demo-map") - -class MapAPIState(rx.State): - current_location: str = "London" - - def fly_to_london(self): - yield map_api.fly_to([51.505, -0.09], 13) - self.current_location = "London" - - def fly_to_paris(self): - yield map_api.fly_to([48.8566, 2.3522], 13) - self.current_location = "Paris" - -def map_api_example(): - return rx.vstack( - rx.text(f"Current location: {MapAPIState.current_location}"), - rx.hstack( - rx.button("Fly to London", on_click=MapAPIState.fly_to_london), - rx.button("Fly to Paris", on_click=MapAPIState.fly_to_paris), - rx.button("Zoom Out", on_click=map_api.set_zoom(8)), - rx.button("Log Center", on_click=map_api.get_center(callback=rx.console_log)), - spacing="2" - ), - rxe.map( - rxe.map.tile_layer( - url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", - attribution='© OpenStreetMap contributors' - ), - id="api-demo-map", - center=rxe.map.latlng(lat=51.505, lng=-0.09), - zoom=13.0, - height="350px", - width="100%", - ), - spacing="3" - ) -``` - -### Common API Methods - -**View Control:** -- `fly_to(latlng, zoom, options)` - Smooth animated movement to location -- `set_view(latlng, zoom, options)` - Instant movement to location -- `set_zoom(zoom)` - Change zoom level -- `zoom_in()` / `zoom_out()` - Zoom by one level -- `fit_bounds(bounds, options)` - Fit map to specific bounds - -**Location Services:** -- `locate(options)` - Get user's current location -- `stop_locate()` - Stop location tracking - -**Information Retrieval:** -- `get_center(callback)` - Get current map center -- `get_zoom(callback)` - Get current zoom level -- `get_bounds(callback)` - Get current map bounds -- `get_size(callback)` - Get map container size - -**Layer Management:** -- `add_layer(layer)` - Add a layer to the map -- `remove_layer(layer)` - Remove a layer from the map -- `has_layer(layer)` - Check if layer exists on map - -### Full Leaflet API Access - -```md alert info -# The Map API provides access to the complete Leaflet map API. Any method available on a Leaflet map instance can be called through the MapAPI instance. -Function names are automatically converted from snake_case (Python) to camelCase (JavaScript). -``` - -This means you can use any method from the [Leaflet Map documentation](https://leafletjs.com/reference.html#map). For example: - -**Python (snake_case) → JavaScript (camelCase):** -- `map_api.pan_to(latlng)` → `map.panTo(latlng)` -- `map_api.set_max_bounds(bounds)` → `map.setMaxBounds(bounds)` -- `map_api.get_pixel_bounds()` → `map.getPixelBounds()` -- `map_api.container_point_to_lat_lng(point)` → `map.containerPointToLatLng(point)` - -### Advanced Example - -```python demo exec -import reflex as rx -import reflex_enterprise as rxe - -class AdvancedMapState(rx.State): - constraints_applied: bool = False - location_tracking: bool = False - location_status: str = "Location tracking disabled" - - def set_location_status(self, status: str): - self.location_status = status - - def setup_map_constraints(self): - map_api = rxe.map.api("advanced-demo-map") - - # Set maximum bounds (restrict panning to London area) - max_bounds = rxe.map.latlng_bounds( - corner1_lat=51.4, corner1_lng=-0.3, - corner2_lat=51.6, corner2_lng=0.1 - ) - yield map_api.set_max_bounds(max_bounds) - - # Set min/max zoom levels - yield map_api.set_min_zoom(10) - yield map_api.set_max_zoom(16) - - # Disable scroll wheel zoom - yield map_api.scroll_wheel_zoom(False) - - self.constraints_applied = True - - def remove_constraints(self): - map_api = rxe.map.api("advanced-demo-map") - - # Remove bounds restriction - yield map_api.set_max_bounds(None) - - # Reset zoom limits - yield map_api.set_min_zoom(1) - yield map_api.set_max_zoom(18) - - # Re-enable scroll wheel zoom - yield map_api.scroll_wheel_zoom(True) - - self.constraints_applied = False - - def toggle_location_tracking(self): - map_api = rxe.map.api("advanced-demo-map") - - if self.location_tracking == False: - # Start location tracking - locate_options = rxe.map.locate_options( - set_view=True, - max_zoom=16, - timeout=10000, - enable_high_accuracy=True, - watch=False # Single location request - ) - yield map_api.locate(locate_options) - self.location_tracking = True - self.location_status = "Requesting location..." - else: - # Stop location tracking - yield map_api.stop_locate() - self.location_tracking = False - self.location_status = "Location tracking disabled" - -def advanced_example(): - return rx.vstack( - rx.hstack( - rx.button( - rx.cond(AdvancedMapState.constraints_applied, "Remove Constraints", "Apply Constraints"), - on_click=rx.cond(AdvancedMapState.constraints_applied, AdvancedMapState.remove_constraints, AdvancedMapState.setup_map_constraints), - color_scheme="blue" - ), - rx.button( - rx.cond(AdvancedMapState.location_tracking, "Disable Location", "Enable Location"), - on_click=AdvancedMapState.toggle_location_tracking, - color_scheme="green" - ), - spacing="3" - ), - rx.text(f"Status: {AdvancedMapState.location_status}"), - rx.text( - rx.cond( - AdvancedMapState.constraints_applied, - "Constraints: Applied (restricted to London area, zoom 10-16, no scroll wheel)", - "Constraints: None" - ) - ), - rxe.map( - rxe.map.tile_layer( - url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", - attribution='© OpenStreetMap contributors' - ), - rxe.map.marker( - rxe.map.popup("Try panning and zooming when constraints are applied!"), - position=rxe.map.latlng(lat=51.505, lng=-0.09), - ), - id="advanced-demo-map", - center=rxe.map.latlng(lat=51.505, lng=-0.09), - zoom=12.0, - height="400px", - width="100%", - on_locationfound=lambda e: AdvancedMapState.set_location_status("Location found!"), - on_locationerror=lambda e: AdvancedMapState.set_location_status("Location error - permission denied or unavailable"), - ), - spacing="3" - ) -``` - -### Callback Handling - -Many API methods that retrieve information require callbacks to handle the results: - -```python -class CallbackMapState(rx.State): - map_info: str = "" - - def handle_center_result(self, result): - lat = result.get("lat", 0) - lng = result.get("lng", 0) - self.map_info = f"Center: {lat:.4f}, {lng:.4f}" - - def handle_bounds_result(self, result): - # result will contain bounds information - self.map_info = f"Bounds: {result}" - - def get_map_info(self): - map_api = rxe.map.api("info-map") - yield map_api.get_center(self.handle_center_result) - # or - yield map_api.get_bounds(self.handle_bounds_result) -``` - -## Available Events - -The map components support a comprehensive set of events: - -**Map Events:** -- `on_click`, `on_dblclick` - Mouse click events -- `on_zoom`, `on_zoom_start`, `on_zoom_end` - Zoom events -- `on_move`, `on_move_start`, `on_move_end` - Pan events -- `on_resize` - Map container resize -- `on_load`, `on_unload` - Map lifecycle - -**Location Events:** -- `on_locationfound`, `on_locationerror` - Geolocation - -**Layer Events:** -- `on_layeradd`, `on_layerremove` - Layer management - -**Popup Events:** -- `on_popupopen`, `on_popupclose` - Popup lifecycle -- `on_tooltipopen`, `on_tooltipclose` - Tooltip lifecycle - -## Common Patterns - -### Dynamic Markers - -```python -class DynamicMapState(rx.State): - markers: list[dict] = [ - {"lat": 51.505, "lng": -0.09, "title": "London"}, - {"lat": 48.8566, "lng": 2.3522, "title": "Paris"}, - {"lat": 52.5200, "lng": 13.4050, "title": "Berlin"}, - ] - -def dynamic_markers(): - return rxe.map( - rxe.map.tile_layer(url="..."), - rx.foreach( - DynamicMapState.markers, - lambda marker: rxe.map.marker( - rxe.map.popup(marker["title"]), - position=rxe.map.latlng( - lat=marker["lat"], - lng=marker["lng"] - ) - ) - ), - # ... map configuration - ) -``` - - -## Best Practices - -1. **Always include attribution** for tile providers -2. **Set reasonable zoom levels** (typically 1-18) -3. **Use bounds for multiple markers** instead of arbitrary center/zoom -4. **Handle loading states** for dynamic map content -5. **Optimize marker rendering** for large datasets using clustering -6. **Test on mobile devices** for touch interactions - ---- - -[← Back to main documentation]({enterprise.overview.path}) diff --git a/docs/enterprise/overview.md b/docs/enterprise/overview.md deleted file mode 100644 index fb58a235e..000000000 --- a/docs/enterprise/overview.md +++ /dev/null @@ -1,295 +0,0 @@ ---- -title: Reflex Enterprise ---- - -# Reflex Enterprise - -```python exec -from pcweb.pages.docs import enterprise -import reflex as rx -try: - import reflex_enterprise as rxe - from reflex_enterprise.components.ag_grid.resource import RendererParams -except ImportError: - rxe = None - RendererParams = None -``` - -Reflex Enterprise is a package containing paid features built on top of Reflex. - -```md alert info -# Despite being an enterprise package, free users can use the components from this package. A badge "Built with Reflex" will be shown in the bottom right corner of the app. -For more information on the badge, visit [Built with Reflex]({enterprise.built_with_reflex.path}). -``` - -## Installation - -`reflex-enterprise` must be installed alongside `reflex` to access the enterprise features. - -You can install it from pypi with the following command: - -```bash -pip install reflex-enterprise -``` - -## Features - -```python exec -# Create master data organized by category -categories_data = [ - { - "category": "Configuration", - "description": "Core enterprise features for deployment and branding", - "count": 2, - "components": [ - { - "feature": "show_built_with_reflex", - "description": "Toggle the 'Built with Reflex' badge in your app", - "cloud_tier": "Enterprise", - "self_hosted_tier": "Enterprise", - "link": "/docs/enterprise/built-with-reflex", - }, - { - "feature": "use_single_port", - "description": "Enable single-port deployment by proxying backend to frontend", - "cloud_tier": "Free", - "self_hosted_tier": "Free", - "link": "/docs/enterprise/single-port-proxy", - }, - ] - }, - { - "category": "AGGrid and AGChart", - "description": "Advanced data visualization and grid components", - "count": 2, - "components": [ - { - "feature": "AgGrid", - "description": "Advanced data grid with enterprise features (sorting, filtering, grouping)", - "cloud_tier": "Free", - "self_hosted_tier": "Free", - "link": "/docs/enterprise/ag-grid", - }, - { - "feature": "AGCharts", - "description": "Interactive charts and data visualization components", - "cloud_tier": "Free", - "self_hosted_tier": "Free", - "link": "/docs/enterprise/ag-chart", - }, - ] - }, - { - "category": "Interactive Components", - "description": "Interactive UI features including drag-and-drop and mapping", - "count": 2, - "components": [ - { - "feature": "Drag and Drop", - "description": "Drag and drop functionality for interactive UI elements", - "cloud_tier": "Free", - "self_hosted_tier": "Free", - "link": "/docs/enterprise/drag-and-drop", - }, - { - "feature": "Mapping", - "description": "Interactive maps with markers, layers, and controls", - "cloud_tier": "Free", - "self_hosted_tier": "Free", - "link": "/docs/enterprise/map", - }, - ] - }, - { - "category": "Mantine", - "description": "Rich UI components from Mantine library", - "count": 15, - "components": [ - { - "feature": "Autocomplete", - "description": "Auto-completing text input with dropdown suggestions", - "cloud_tier": "Free", - "self_hosted_tier": "Free", - "link": "/docs/enterprise/mantine/autocomplete", - }, - { - "feature": "Combobox", - "description": "Searchable dropdown with custom options and filtering", - "cloud_tier": "Free", - "self_hosted_tier": "Free", - "link": "/docs/enterprise/mantine/combobox", - }, - { - "feature": "Multi Select", - "description": "Multi-selection dropdown with tags and search", - "cloud_tier": "Free", - "self_hosted_tier": "Free", - "link": "/docs/enterprise/mantine/multi-select", - }, - { - "feature": "Tags Input", - "description": "Input field for creating and managing tags", - "cloud_tier": "Free", - "self_hosted_tier": "Free", - "link": "/docs/enterprise/mantine/tags-input", - }, - { - "feature": "Json Input", - "description": "JSON editor with syntax highlighting and validation", - "cloud_tier": "Free", - "self_hosted_tier": "Free", - "link": "/docs/enterprise/mantine/json-input", - }, - { - "feature": "Pill", - "description": "Small rounded elements for tags, badges, and labels", - "cloud_tier": "Free", - "self_hosted_tier": "Free", - "link": "/docs/enterprise/mantine/pill", - }, - { - "feature": "Tree", - "description": "Hierarchical tree view with expandable nodes", - "cloud_tier": "Free", - "self_hosted_tier": "Free", - "link": "/docs/enterprise/mantine/tree", - }, - { - "feature": "Timeline", - "description": "Timeline component for displaying chronological events", - "cloud_tier": "Free", - "self_hosted_tier": "Free", - "link": "/docs/enterprise/mantine/timeline", - }, - { - "feature": "Number Formatter", - "description": "Format and display numbers with customizable formatting", - "cloud_tier": "Free", - "self_hosted_tier": "Free", - "link": "/docs/enterprise/mantine/number-formatter", - }, - { - "feature": "Ring Progress", - "description": "Circular progress indicator with customizable styling", - "cloud_tier": "Free", - "self_hosted_tier": "Free", - "link": "/docs/enterprise/mantine/ring-progress", - }, - { - "feature": "Semi Circle Progress", - "description": "Semi-circular progress indicator for dashboards", - "cloud_tier": "Free", - "self_hosted_tier": "Free", - "link": "/docs/enterprise/mantine/semi-circle-progress", - }, - { - "feature": "Loading Overlay", - "description": "Loading overlay with spinner for async operations", - "cloud_tier": "Free", - "self_hosted_tier": "Free", - "link": "/docs/enterprise/mantine/loading-overlay", - }, - { - "feature": "Spoiler", - "description": "Collapsible content container with show/hide toggle", - "cloud_tier": "Free", - "self_hosted_tier": "Free", - "link": "/docs/enterprise/mantine/spoiler", - }, - { - "feature": "Collapse", - "description": "Animated collapsible content with smooth transitions", - "cloud_tier": "Free", - "self_hosted_tier": "Free", - "link": "/docs/enterprise/mantine/collapse", - }, - ] - }, -] - -if rxe is not None: - @rxe.arrow_func - def custom_link_renderer(params: RendererParams): - """Custom cell renderer for links in AG Grid.""" - return rx.link( - params.value, - href=params.data.link, - ) - - grid = rxe.ag_grid( - column_defs=[ - { - "field": "category", - "header_name": "Category", - "cell_renderer": "agGroupCellRenderer", - "suppress_menu": True, - "width": 220, - }, - { - "field": "description", - "width": 500, - }, - { - "field": "count", - "header_name": "Components", - "width": 150, - }, - ], - row_data=categories_data, - master_detail=True, - detail_cell_renderer_params={ - "detail_grid_options": { - "column_defs": [ - { - "field": "feature", - "header_name": "Component/Feature", - "cell_renderer": custom_link_renderer, - "width": 250 - }, - {"field": "description", "header_name": "Description", "width": 350}, - {"field": "cloud_tier", "header_name": "Cloud Tier", "width": 120}, - {"field": "self_hosted_tier", "header_name": "Self-hosted Tier", "width": 140}, - ], - "suppress_context_menu": True, - "row_height": 35, - }, - "get_detail_row_data": lambda params: rx.vars.function.FunctionStringVar( - "params.successCallback" - ).call(params.data.components), - }, - id="features-grid", - width="100%", - detail_row_height=200, - detail_row_auto_height=True, - height="400px", - loading=False, -) - grid.api.set_grid_option("suppressContextMenu", True) -else: - grid = rx.text("Reflex Enterprise not available. Install with: pip install reflex-enterprise") -``` - -```python eval -grid -``` - -## Usage of reflex_enterprise. - -Using `rxe.App` as your `app` is required to use any of the components provided by the enterprise package, as well as config options provided by `rxe.Config`. - -### In the main file - -Instead of the usual `rx.App()` to create your app, use the following: -```python -import reflex_enterprise as rxe -app = rxe.App() -``` - -### In rxconfig.py -```python -import reflex_enterprise as rxe -config = rxe.Config( - app_name="MyApp", - ... # you can pass all rx.Config arguments as well as the one specific to rxe.Config -) -``` \ No newline at end of file diff --git a/docs/enterprise/react_flow/basic_flow.md b/docs/enterprise/react_flow/basic_flow.md deleted file mode 100644 index b057fdc91..000000000 --- a/docs/enterprise/react_flow/basic_flow.md +++ /dev/null @@ -1,96 +0,0 @@ -# Basic Flow Example - -This example demonstrates a simple flow diagram with three nodes and two edges, showing how nodes can be connected and how edges can be animated. - -### Nodes - -- Input Node – starting point -- Default Node – standard content node -- Output Node – endpoint of the flow - -### Edges - -- Input → Default (animated) -- Default → Output - -### Interactivity - -- Nodes can be moved -- Edges update dynamically -- Users can drag from handles to create new edges -- Zoom, pan, and mini-map controls are available - -### Visual Layout - -- Flow fits viewport automatically -- Background grid for orientation -- Light and dark color modes supported - -### Example Flow - -```python demo exec -import reflex as rx -import reflex_enterprise as rxe -from reflex_enterprise.components.flow.types import Node, Edge - -# Common style for all nodes -node_style = { - "backgroundColor": "#ffcc00", - "color": "#000000", - "padding": "10px", - "borderRadius": "5px" -} - -class FlowState(rx.State): - nodes: list[Node] = [ - { - "id": "1", - "type": "input", - "position": {"x": 100, "y": 100}, - "data": {"label": "Input Node"}, - "style": node_style - }, - { - "id": "2", - "type": "default", - "position": {"x": 300, "y": 200}, - "data": {"label": "Default Node"}, - "style": node_style - }, - { - "id": "3", - "type": "output", - "position": {"x": 500, "y": 100}, - "data": {"label": "Output Node"}, - "style": node_style - }, - ] - - edges: list[Edge] = [ - {"id": "e1-2", "source": "1", "target": "2", "animated": True}, - {"id": "e2-3", "source": "2", "target": "3"}, - ] - - -def flow_example(): - return rx.box( - rxe.flow( - # Core flow components - rxe.flow.controls(), - rxe.flow.background(), - rxe.flow.mini_map(), - - # Flow configuration - default_nodes=FlowState.nodes, - default_edges=FlowState.edges, - nodes=FlowState.nodes, - edges=FlowState.edges, - - # Visual settings - fit_view=True, - attribution_position="bottom-right", - ), - height="100vh", - width="100vw", - ) -``` diff --git a/docs/enterprise/react_flow/components.md b/docs/enterprise/react_flow/components.md deleted file mode 100644 index 04ba05361..000000000 --- a/docs/enterprise/react_flow/components.md +++ /dev/null @@ -1,92 +0,0 @@ -# Flow Components - -This page documents the main components provided by the `rxe.flow` library. - -## rxe.flow.provider - -The `FlowProvider` component is a context provider that makes it possible to access a flow’s internal state outside of the `` component. Many of the hooks we provide rely on this component to work. - -**Props:** - -- `initial_nodes`: `Sequence[Node]` - These nodes are used to initialize the flow. They are not dynamic. -- `default_edges`: `Sequence[Edge]` - These edges are used to initialize the flow. They are not dynamic. -- `initial_width`: `float` - The initial width is necessary to be able to use fitView on the server. -- `initial_height`: `float` - The initial height is necessary to be able to use fitView on the server. -- `fit_view`: `bool` - When true, the flow will be zoomed and panned to fit all the nodes initially provided. -- `initial_fit_view_options`: `FitViewOptions` - You can provide an object of options to customize the initial fitView behavior. -- `initial_min_zoom`: `float` - Initial minimum zoom level. -- `initial_max_zoom`: `float` - Initial maximum zoom level. -- `node_origin`: `NodeOrigin` - The origin of the node to use when placing it in the flow or looking up its x and y position. -- `node_extent`: `CoordinateExtent` - The boundary a node can be moved in. - -## rxe.flow - -The `Flow` component is the main component that renders the flow. It takes in nodes and edges, and provides event handlers for user interactions. - -**Props:** - -- `nodes`: `Sequence[Node]` - An array of nodes to render in a controlled flow. -- `edges`: `Sequence[Edge]` - An array of edges to render in a controlled flow. -- `default_nodes`: `Sequence[Node]` - The initial nodes to render in an uncontrolled flow. -- `default_edges`: `Sequence[Edge]` - The initial edges to render in an uncontrolled flow. -- `node_types`: `Mapping[str, Any]` - Custom node types. -- `edge_types`: `Mapping[str, Any]` - Custom edge types. -- `on_nodes_change`: Event handler for when nodes change. -- `on_edges_change`: Event handler for when edges change. -- `on_connect`: Event handler for when a connection is made. -- `fit_view`: `bool` - When true, the flow will be zoomed and panned to fit all the nodes initially provided. -- `fit_view_options`: `FitViewOptions` - Options for `fit_view`. -- `style`: The style of the component. - -## rxe.flow.background - -The `Background` component renders a background for the flow. It can be a pattern of lines, dots, or a cross. - -**Props:** - -- `color`: `str` - Color of the pattern. -- `bg_color`: `str` - Color of the background. -- `variant`: `Literal["lines", "dots", "cross"]` - The type of pattern to render. -- `gap`: `float | tuple[float, float]` - The gap between patterns. -- `size`: `float` - The size of the pattern elements. - -**Example:** - -```python -rxe.flow.background(variant="dots", gap=20, size=1) -``` - -## rxe.flow.controls - -The `Controls` component renders a panel with buttons to zoom in, zoom out, fit the view, and lock the viewport. - -**Props:** - -- `show_zoom`: `bool` - Whether to show the zoom buttons. -- `show_fit_view`: `bool` - Whether to show the fit view button. -- `show_interactive`: `bool` - Whether to show the lock button. -- `position`: `PanelPosition` - The position of the controls on the pane. - -**Example:** - -```python -rxe.flow.controls() -``` - -## rxe.flow.mini_map - -The `MiniMap` component renders a small overview of your flow. - -**Props:** - -- `node_color`: `str | Any` - Color of nodes on minimap. -- `node_stroke_color`: `str | Any` - Stroke color of nodes on minimap. -- `pannable`: `bool` - Determines whether you can pan the viewport by dragging inside the minimap. -- `zoomable`: `bool` - Determines whether you can zoom the viewport by scrolling inside the minimap. -- `position`: `PanelPosition` - Position of minimap on pane. - -**Example:** - -```python -rxe.flow.mini_map(pannable=True, zoomable=True) -``` diff --git a/docs/enterprise/react_flow/edges.md b/docs/enterprise/react_flow/edges.md deleted file mode 100644 index 25142f70a..000000000 --- a/docs/enterprise/react_flow/edges.md +++ /dev/null @@ -1,217 +0,0 @@ -# Edges - -Edges connect nodes together in a flow. This page explains how to define, customize, and interact with edges in Reflex Flow. - -## The Edge Type - -An edge is represented as a Python dictionary with the following fields: - -- `id` (`str`) – Unique identifier for the edge. -- `source` (`str`) – ID of the source node. -- `target` (`str`) – ID of the target node. -- `type` (`str`) – Edge type defined in `edge_types`. -- `sourceHandle` (`str | None`) – Optional source handle ID. -- `targetHandle` (`str | None`) – Optional target handle ID. -- `animated` (`bool`) – Whether the edge should animate. -- `hidden` (`bool`) – Whether the edge is hidden. -- `deletable` (`bool`) – Whether the edge can be removed. -- `selectable` (`bool`) – Whether the edge can be selected. -- `data` (`dict`) – Arbitrary metadata. -- `label` (`Any`) – Label rendered along the edge. -- `style` (`dict`) – Custom styles. -- `className` (`str`) – CSS class for the edge. - -## Basic Edge Types - -Reflex Flow comes with several built-in edge types: - -### Default Edge Types - -```python -edges: list[Edge] = [ - {"id": "e1", "source": "1", "target": "2", "type": "default"}, - {"id": "e2", "source": "2", "target": "3", "type": "straight"}, - {"id": "e3", "source": "3", "target": "4", "type": "step"}, - {"id": "e4", "source": "4", "target": "5", "type": "smoothstep"}, - {"id": "e5", "source": "5", "target": "6", "type": "bezier"}, -] -``` - -- **default** – Standard curved edge -- **straight** – Direct line between nodes -- **step** – Right-angled path with steps -- **smoothstep** – Smooth right-angled path -- **bezier** – Curved bezier path - -## Edge Styling - -### Basic Styling - -Add visual styling to edges using the `style` property: - -```python -edges: list[Edge] = [ - { - "id": "styled-edge", - "source": "1", - "target": "2", - "style": { - "stroke": "#ff6b6b", - "strokeWidth": 3, - } - } -] -``` - -### Animated Edges - -Make edges animate with flowing dots: - -```python -edges: list[Edge] = [ - { - "id": "animated-edge", - "source": "1", - "target": "2", - "animated": True, - "style": {"stroke": "#4dabf7"} - } -] -``` - -### Edge Labels - -Add text labels to edges: - -```python -edges: list[Edge] = [ - { - "id": "labeled-edge", - "source": "1", - "target": "2", - "label": "Connection", - "style": {"stroke": "#51cf66"} - } -] -``` - -# Custom Edges - -React Flow in Reflex also allows you to define custom edge types. This is useful when you want edges to carry extra functionality (like buttons, labels, or dynamic styling) beyond the default straight or bezier connectors. - -```python demo exec -import reflex as rx -import reflex_enterprise as rxe -from reflex_enterprise.components.flow.types import ( - ConnectionInProgress, - Edge, - NoConnection, - Node, - Position, -) - -class SimpleEdgeDemoState(rx.State): - nodes: list[Node] = [ - {"id": "1", "position": {"x": 0, "y": 0}, "data": {"label": "Node A"}, "style": {"color": "#000000",}}, - {"id": "2", "position": {"x": 250, "y": 150}, "data": {"label": "Node B"}, "style": {"color": "#000000",}}, - ] - edges: list[Edge] = [ - {"id": "e1-2", "source": "1", "target": "2", "type": "button"} - ] - - @rx.event - def set_nodes(self, nodes: list[Node]): - self.nodes = nodes - - @rx.event - def set_edges(self, edges: list[Edge]): - self.edges = edges - - @rx.event - def handle_connect_end( - self, - connection_status: NoConnection | ConnectionInProgress, - ): - if not connection_status["isValid"]: - new_edge = { - "id": f"{connection_status['fromNode']['id']}-{connection_status['toNode']['id']}", - "source": connection_status["fromNode"]["id"], - "target": connection_status["toNode"]["id"], - "type": "button", - } - self.edges.append(new_edge) - - - -@rx.memo -def button_edge( - id: rx.Var[str], - sourceX: rx.Var[float], - sourceY: rx.Var[float], - targetX: rx.Var[float], - targetY: rx.Var[float], - sourcePosition: rx.Var[Position], - targetPosition: rx.Var[Position], - markerEnd: rx.Var[str], -): - bezier_path = rxe.components.flow.util.get_bezier_path( - source_x=sourceX, - source_y=sourceY, - target_x=targetX, - target_y=targetY, - source_position=sourcePosition, - target_position=targetPosition, - ) - - mid_x = bezier_path.label_x - mid_y = bezier_path.label_y - - return rx.fragment( - rxe.flow.base_edge(path=bezier_path.path, markerEnd=markerEnd), - rxe.flow.edge_label_renderer( - rx.el.div( - rx.el.button( - "×", - class_name=("w-[30px] h-[30px] border-2 border-gray-200 bg-gray-200 text-black rounded-full text-[12px] pt-0 cursor-pointer hover:bg-gray-400 hover:text-white"), - on_click=rx.run_script( - rxe.flow.api.set_edges( - rx.vars.FunctionStringVar.create( - "Array.prototype.filter.call" - ).call( - rxe.flow.api.get_edges(), - rx.Var(f"((edge) => edge.id !== {id})"), - ), - ) - ), - style={ - "position": "absolute", - "left": f"{mid_x}px", - "top": f"{mid_y}px", - "transform": "translate(-50%, -50%)", - "pointerEvents": "all", - }, - ), - ) - ), - ) - -def very_simple_custom_edge_example(): - return rx.box( - rxe.flow( - rxe.flow.background(), - default_nodes=SimpleEdgeDemoState.nodes, - default_edges=SimpleEdgeDemoState.edges, - nodes=SimpleEdgeDemoState.nodes, - edges=SimpleEdgeDemoState.edges, - on_connect=lambda connection: SimpleEdgeDemoState.set_edges( - rxe.flow.util.add_edge(connection, SimpleEdgeDemoState.edges) - ), - on_connect_end=lambda status, event: SimpleEdgeDemoState.handle_connect_end(status), - edge_types={"button": button_edge}, - fit_view=True, - ), - height="100vh", - width="100%", - ) - -``` diff --git a/docs/enterprise/react_flow/examples.md b/docs/enterprise/react_flow/examples.md deleted file mode 100644 index 72d4a0e76..000000000 --- a/docs/enterprise/react_flow/examples.md +++ /dev/null @@ -1,235 +0,0 @@ -# Example React Flow Components - -This section showcases examples of interactive flow components built with Reflex and Reflex Enterprise. Learn how to create dynamic nodes, edges, and custom behaviors for building flow diagrams in your React apps. - -## Add Node on Edge Drop - -In this example, we demonstrate how to dynamically add nodes to a flow when a connection is dropped onto the canvas. When the user drops a connection, a new node is created at the drop point, and an edge is added between the source node and the new node. - - -```python demo exec -import reflex as rx - -import reflex_enterprise as rxe -from reflex_enterprise.components.flow.types import ( - ConnectionInProgress, - Edge, - NoConnection, - Node, - XYPosition, -) - -node_style = { - "color": "#000000", -} - -initial_nodes: list[Node] = [ - { - "id": "0", - "type": "input", - "data": {"label": "Node"}, - "position": {"x": 0, "y": 50}, - "style": node_style - }, -] - - -class AddNodesOnEdgeDropState(rx.State): - nodes: rx.Field[list[Node]] = rx.field(default_factory=lambda: initial_nodes) - edges: rx.Field[list[Edge]] = rx.field(default_factory=list) - node_id: int = 1 - - @rx.event - def increment(self): - self.node_id += 1 - - @rx.event - def set_nodes(self, nodes: list[Node]): - self.nodes = nodes - - @rx.event - def set_edges(self, edges: list[Edge]): - self.edges = edges - - @rx.event - def handle_connect_end( - self, - connection_status: NoConnection | ConnectionInProgress, - event: rx.event.PointerEventInfo, - flow_position: XYPosition, - ): - if not connection_status["isValid"]: - node_id = str(self.node_id) - self.increment() - self.nodes.append( - { - "id": node_id, - "position": flow_position, - "data": {"label": f"Node {node_id}"}, - "origin": (0.5, 0.0), - "style": node_style - } - ) - self.edges.append( - { - "id": node_id, - "source": connection_status["fromNode"]["id"], - "target": node_id, - "style": node_style - } - ) - -def add_node_on_edge_drop(): - return rx.box( - rxe.flow.provider( - rxe.flow( - rxe.flow.controls(), - rxe.flow.mini_map(), - rxe.flow.background(), - on_connect=lambda connection: AddNodesOnEdgeDropState.set_edges( - rxe.flow.util.add_edge(connection, AddNodesOnEdgeDropState.edges) - ), - on_connect_end=( - lambda connection_status, event: ( - AddNodesOnEdgeDropState.handle_connect_end( - connection_status, - event, - rxe.flow.api.screen_to_flow_position( - x=event.client_x, - y=event.client_y, - ), - ) - ) - ), - nodes=AddNodesOnEdgeDropState.nodes, - edges=AddNodesOnEdgeDropState.edges, - default_nodes=AddNodesOnEdgeDropState.nodes, - default_edges=AddNodesOnEdgeDropState.edges, - on_nodes_change=lambda changes: AddNodesOnEdgeDropState.set_nodes( - rxe.flow.util.apply_node_changes( - AddNodesOnEdgeDropState.nodes, changes - ) - ), - on_edges_change=lambda changes: AddNodesOnEdgeDropState.set_edges( - rxe.flow.util.apply_edge_changes( - AddNodesOnEdgeDropState.edges, changes - ) - ), - fit_view=True, - fit_view_options={"padding": 2}, - node_origin=(0.5, 0.0), - ) - ), - height="100vh", - width="100vw", - ) - -``` - -## Connection Limit on Custom Node - -This example demonstrates how to create a custom node with a connection limit on its handle. The handle can be configured to allow a specific number of connections, or no connections at all, using the isConnectable property. This is useful when you want to restrict the number of connections a node can have. - -```python demo exec -import reflex as rx - -import reflex_enterprise as rxe -from reflex_enterprise.components.flow.types import Edge, HandleType, Node, Position - -node_style = { - "color": "#000000", -} - -class ConnectionLimitState(rx.State): - nodes: rx.Field[list[Node]] = rx.field( - default_factory=lambda: [ - { - "id": "1", - "type": "input", - "data": {"label": "Node 1"}, - "position": {"x": 0, "y": 25}, - "sourcePosition": "right", - "style": node_style - }, - { - "id": "2", - "type": "custom", - "data": {}, - "position": {"x": 250, "y": 50}, - "style": node_style - }, - { - "id": "3", - "type": "input", - "data": {"label": "Node 2"}, - "position": {"x": 0, "y": 100}, - "sourcePosition": "right", - "style": node_style - }, - ] - ) - edges: rx.Field[list[Edge]] = rx.field(default_factory=list) - - @rx.event - def set_nodes(self, nodes: list[Node]): - self.nodes = nodes - - @rx.event - def set_edges(self, edges: list[Edge]): - self.edges = edges - - -@rx.memo -def custom_handle( - type: rx.Var[HandleType], position: rx.Var[Position], connection_count: rx.Var[int] -): - connections = rxe.flow.api.get_node_connections() - return rxe.flow.handle( - type=type, - position=position, - connection_count=connection_count, - is_connectable=connections.length() < connection_count.guess_type(), - ) - - -@rx.memo -def custom_node(): - return rx.el.div( - custom_handle(type="target", position="left", connection_count=1), - rx.el.div("← Only one edge allowed"), - class_name="border border-1 p-2 rounded-sm", - border_color=rx.color_mode_cond("black", ""), - color="black", - bg="white", - ) - - -def connection_limit(): - return rx.box( - rxe.flow( - rxe.flow.background(), - nodes=ConnectionLimitState.nodes, - edges=ConnectionLimitState.edges, - default_nodes=ConnectionLimitState.nodes, - default_edges=ConnectionLimitState.edges, - on_nodes_change=lambda changes: ConnectionLimitState.set_nodes( - rxe.flow.util.apply_node_changes(ConnectionLimitState.nodes, changes) - ), - on_edges_change=lambda changes: ConnectionLimitState.set_edges( - rxe.flow.util.apply_edge_changes(ConnectionLimitState.edges, changes) - ), - on_connect=lambda connection: ConnectionLimitState.set_edges( - rxe.flow.util.add_edge(connection, ConnectionLimitState.edges) - ), - node_types={ - "custom": rx.vars.function.ArgsFunctionOperation.create( - (), custom_node() - ) - }, - color_mode="light", - fit_view=True, - ), - height="100vh", - width="100vw", - ) -``` diff --git a/docs/enterprise/react_flow/hooks.md b/docs/enterprise/react_flow/hooks.md deleted file mode 100644 index e9d12d953..000000000 --- a/docs/enterprise/react_flow/hooks.md +++ /dev/null @@ -1,33 +0,0 @@ -# Hooks (API) - -The `rxe.flow.api` module provides hooks to interact with the Flow instance. These hooks are wrappers around the `useReactFlow` hook from React Flow. - -## Node Hooks - -- `get_nodes()`: Returns an array of all nodes in the flow. -- `set_nodes(nodes)`: Sets the nodes in the flow. -- `add_nodes(nodes)`: Adds nodes to the flow. -- `get_node(id)`: Returns a node by its ID. -- `update_node(id, node_update, replace=False)`: Updates a node in the flow. -- `update_node_data(id, data_update, replace=False)`: Updates a node's data in the flow. - -## Edge Hooks - -- `get_edges()`: Returns an array of all edges in the flow. -- `set_edges(edges)`: Sets the edges in the flow. -- `add_edges(edges)`: Adds edges to the flow. -- `get_edge(id)`: Returns an edge by its ID. -- `update_edge(id, edge_update, replace=False)`: Updates an edge in the flow. -- `update_edge_data(id, data_update, replace=False)`: Updates an edge's data in the flow. - -## Viewport Hooks - -- `screen_to_flow_position(x, y, snap_to_grid=False)`: Translates a screen pixel position to a flow position. -- `flow_to_screen_position(x, y)`: Translates a position inside the flow’s canvas to a screen pixel position. - -## Other Hooks - -- `to_object()`: Converts the React Flow state to a JSON object. -- `get_intersecting_nodes(node, partially=True, nodes=None)`: Find all the nodes currently intersecting with a given node or rectangle. -- `get_node_connections(id=None, handle_type=None, handle_id=None)`: This hook returns an array of connections on a specific node, handle type ("source", "target") or handle ID. -- `get_connection()`: Returns the current connection state when there is an active connection interaction. diff --git a/docs/enterprise/react_flow/interactivity.md b/docs/enterprise/react_flow/interactivity.md deleted file mode 100644 index 526913608..000000000 --- a/docs/enterprise/react_flow/interactivity.md +++ /dev/null @@ -1,71 +0,0 @@ -# Adding Interactivity to Your Flow - -This guide shows how to create an interactive flow in Reflex, allowing you to select, drag, and connect nodes and edges. - -## Define the State - -We start by defining the nodes and edges of the flow. The `FlowState` class holds the nodes and edges as state variables and includes event handlers to respond to changes. - -```python -import reflex as rx -import reflex_enterprise as rxe -from reflex_enterprise.components.flow.types import Node, Edge - -class FlowState(rx.State): - nodes: list[Node] = [ - {"id": "1", "type": "input", "position": {"x": 100, "y": 100}, "data": {"label": "Node 1"}}, - {"id": "2", "type": "default", "position": {"x": 300, "y": 200}, "data": {"label": "Node 2"}}, - ] - - edges: list[Edge] = [ - {"id": "e1-2", "source": "1", "target": "2", "label": "Connection", "type": "step"} - ] -``` - -# Add Event Handlers - -Event handlers allow the flow to respond to user interactions such as dragging nodes, updating edges, or creating new connections. - -```python -@rx.event -def set_nodes(self, nodes: list[Node]): - self.nodes = nodes - -@rx.event -def set_edges(self, edges: list[Edge]): - self.edges = edges - -``` - -- set_nodes updates nodes when they are moved or edited. - -- set_edges updates edges when they are modified or deleted. - - -## Render the Interactive Flow - -Finally, we render the flow using **rxe.flow**, passing in the state and event handlers. Additional UI features include zoom/pan controls, a background grid, and a mini-map for navigation. - -```python -def interactive_flow(): - return rx.box( - rxe.flow( - rxe.flow.controls(), - rxe.flow.background(), - rxe.flow.mini_map(), - nodes=FlowState.nodes, - edges=FlowState.edges, - on_nodes_change=lambda node_changes: FlowState.set_nodes( - rxe.flow.util.apply_node_changes(FlowState.nodes, node_changes) - ), - on_edges_change=lambda edge_changes: FlowState.set_edges( - rxe.flow.util.apply_edge_changes(FlowState.edges, edge_changes) - ), - fit_view=True, - - attribution_position="bottom-right", - ), - height="100vh", - width="100vw", - ) -``` diff --git a/docs/enterprise/react_flow/nodes.md b/docs/enterprise/react_flow/nodes.md deleted file mode 100644 index 398afdfdd..000000000 --- a/docs/enterprise/react_flow/nodes.md +++ /dev/null @@ -1,277 +0,0 @@ -# Nodes - -Nodes are the fundamental building blocks of a flow. This page explains how to define and customize nodes in Reflex Flow. - -## The Node Type - -A node is represented as a Python dictionary with the following fields: - -- `id` (`str`) – Unique identifier for the node. -- `position` (`dict`) – Position of the node with `x` and `y` coordinates. -- `data` (`dict`) – Arbitrary data passed to the node component. -- `type` (`str`) – Node type defined in `node_types`. -- `sourcePosition` (`str`) – Controls source handle position ("top", "right", "bottom", "left"). -- `targetPosition` (`str`) – Controls target handle position ("top", "right", "bottom", "left"). -- `hidden` (`bool`) – Whether the node is visible on the canvas. -- `selected` (`bool`) – Whether the node is currently selected. -- `draggable` (`bool`) – Whether the node can be dragged. -- `selectable` (`bool`) – Whether the node can be selected. -- `connectable` (`bool`) – Whether the node can be connected to other nodes. -- `deletable` (`bool`) – Whether the node can be deleted. -- `width` (`float`) – Width of the node. -- `height` (`float`) – Height of the node. -- `parentId` (`str`) – Parent node ID for creating sub-flows. -- `style` (`dict`) – Custom styles for the node. -- `className` (`str`) – CSS class name for the node. - -## Built-in Node Types - -Reflex Flow includes several built-in node types: - -```python -nodes: list[Node] = [ - {"id": "1", "type": "input", "position": {"x": 100, "y": 100}, "data": {"label": "Start"}}, - {"id": "2", "type": "default", "position": {"x": 300, "y": 100}, "data": {"label": "Process"}}, - {"id": "3", "type": "output", "position": {"x": 500, "y": 100}, "data": {"label": "End"}}, -] -``` - -- **input** – Entry point with only source handles -- **default** – Standard node with both source and target handles -- **output** – Exit point with only target handles - -## Basic Node Configuration - -### Node Positioning - -```python -node = { - "id": "positioned-node", - "type": "default", - "position": {"x": 250, "y": 150}, - "data": {"label": "Positioned Node"} -} -``` - -### Node Styling - -```python -styled_node = { - "id": "styled-node", - "type": "default", - "position": {"x": 100, "y": 200}, - "data": {"label": "Custom Style"}, - "style": { - "background": "#ff6b6b", - "color": "white", - "border": "2px solid #ff5252", - "borderRadius": "8px", - "padding": "10px" - } -} -``` - -### Handle Positioning - -```python -node_with_handles = { - "id": "handle-node", - "type": "default", - "position": {"x": 300, "y": 300}, - "data": {"label": "Custom Handles"}, - "sourcePosition": "right", - "targetPosition": "left" -} -``` - -# Custom Nodes - -Creating custom nodes is as easy as building a regular React component and passing it to the `node_types`. Since they’re standard React components, you can display any content and implement any functionality you need. Plus, you’ll have access to a range of props that allow you to extend and customize the default node behavior. - -Below is an example custom node using a `color picker` component. - -```python demo exec - -from typing import Any - -import reflex as rx - -import reflex_enterprise as rxe -from reflex_enterprise.components.flow.types import Connection, Edge, Node - - -class CustomNodeState(rx.State): - bg_color: rx.Field[str] = rx.field(default="#c9f1dd") - nodes: rx.Field[list[Node]] = rx.field( - default_factory=lambda: [ - { - "id": "1", - "type": "input", - "data": {"label": "An input node"}, - "position": {"x": 0, "y": 50}, - "sourcePosition": "right", - "style": {"color": "#000000",} - }, - { - "id": "2", - "type": "selectorNode", - "data": { - "color": "#c9f1dd", - }, - "position": {"x": 300, "y": 50}, - }, - { - "id": "3", - "type": "output", - "data": {"label": "Output A"}, - "position": {"x": 650, "y": 25}, - "targetPosition": "left", - "style": {"color": "#000000",} - }, - { - "id": "4", - "type": "output", - "data": {"label": "Output B"}, - "position": {"x": 650, "y": 100}, - "targetPosition": "left", - "style": {"color": "#000000",} - }, - ] - ) - edges: rx.Field[list[Edge]] = rx.field( - default_factory=lambda: [ - { - "id": "e1-2", - "source": "1", - "target": "2", - "animated": True, - }, - { - "id": "e2a-3", - "source": "2", - "target": "3", - "animated": True, - }, - { - "id": "e2b-4", - "source": "2", - "target": "4", - "animated": True, - }, - ] - ) - - @rx.event - def on_change_color(self, color: str): - self.nodes = [ - node - if node["id"] != "2" or "data" not in node - else {**node, "data": {**node["data"], "color": color}} - for node in self.nodes - ] - self.bg_color = color - - @rx.event - def set_nodes(self, nodes: list[Node]): - self.nodes = nodes - - @rx.event - def set_edges(self, edges: list[Edge]): - self.edges = edges - - -@rx.memo -def color_selector_node(data: rx.Var[dict], isConnectable: rx.Var[bool]): - data = data.to(dict) - return rx.el.div( - rxe.flow.handle( - type="target", - position="left", - is_connectable=isConnectable, - ), - rx.el.div( - "Custom Color Picker Node: ", - rx.el.strong(data["color"]), - ), - rx.el.input( - class_name="nodrag", - type="color", - on_change=CustomNodeState.on_change_color, - default_value=data["color"], - ), - rxe.flow.handle( - type="source", - position="right", - is_connectable=isConnectable, - ), - class_name="border border-1 p-2 rounded-sm", - border_color=rx.color_mode_cond("black", ""), - color="black", - bg="white", - ) - - -def node_stroke_color(node: rx.vars.ObjectVar[Node]): - return rx.match( - node["type"], - ("input", "#0041d0"), - ( - "selectorNode", - CustomNodeState.bg_color, - ), - ("output", "#ff0072"), - None, - ) - - -def node_color(node: rx.vars.ObjectVar[Node]): - return rx.match( - node["type"], - ( - "selectorNode", - CustomNodeState.bg_color, - ), - "#fff", - ) - -def custom_node(): - return rx.box( - rxe.flow( - rxe.flow.background(bg_color=CustomNodeState.bg_color), - rxe.flow.mini_map( - node_stroke_color=rx.vars.function.ArgsFunctionOperation.create( - ("node",), node_stroke_color(rx.Var("node").to(Node)) - ), - node_color=rx.vars.function.ArgsFunctionOperation.create( - ("node",), node_color(rx.Var("node").to(Node)) - ), - ), - rxe.flow.controls(), - default_nodes=CustomNodeState.nodes, - default_edges=CustomNodeState.edges, - nodes=CustomNodeState.nodes, - edges=CustomNodeState.edges, - on_nodes_change=lambda changes: CustomNodeState.set_nodes( - rxe.flow.util.apply_node_changes(CustomNodeState.nodes, changes) - ), - on_edges_change=lambda changes: CustomNodeState.set_edges( - rxe.flow.util.apply_edge_changes(CustomNodeState.edges, changes) - ), - on_connect=lambda connection: CustomNodeState.set_edges( - rxe.flow.util.add_edge( - connection.to(dict).merge({"animated": True}).to(Connection), - CustomNodeState.edges, - ) - ), - node_types={"selectorNode": color_selector_node}, - color_mode="light", - snap_grid=(20, 20), - default_viewport={"x": 0, "y": 0, "zoom": 1.5}, - snap_to_grid=True, - attribution_position="bottom-left", - fit_view=True, - ), - height="100vh", - width="100vw", - ) -``` diff --git a/docs/enterprise/react_flow/overview.md b/docs/enterprise/react_flow/overview.md deleted file mode 100644 index 8c4e18364..000000000 --- a/docs/enterprise/react_flow/overview.md +++ /dev/null @@ -1,23 +0,0 @@ -# Overview - -At its core, a flow diagram is an interactive graph composed of nodes connected by edges. To help understand the key concepts, let’s go over the main components of a flow. - -### Nodes - -Nodes are the building blocks of a flow. While there are a few default node types available, the real power comes from customizing them. You can design nodes to include interactive elements, display dynamic data, or support multiple connection points. The framework provides the foundation—you provide the functionality and style. - -### Handles - -Handles are the points on a node where edges attach. They typically appear on the top, bottom, left, or right sides of a node, but can be positioned and styled freely. Nodes can have multiple handles, allowing for complex connection setups. - -### Edges - -Edges are the connections between nodes. Each edge requires a source node and a target node. Edges can be styled and customized, and nodes with multiple handles can support multiple edges. Custom edges can include interactive elements, specialized routing, or unique visual styles beyond simple lines. - -### Connection Line - -When creating a new edge, you can click and drag from one handle to another. While dragging, the placeholder edge is called a connection line. Connection lines behave like edges and can be customized in appearance and behavior. - -### Viewport - -The viewport is the visible area containing the flow. Each node has x- and y-coordinates representing its position. Moving the viewport changes these coordinates, and zooming in or out adjusts the zoom level. The viewport ensures the diagram remains navigable and interactive. diff --git a/docs/enterprise/react_flow/theming.md b/docs/enterprise/react_flow/theming.md deleted file mode 100644 index 3aa8a1d2f..000000000 --- a/docs/enterprise/react_flow/theming.md +++ /dev/null @@ -1,74 +0,0 @@ -# Theming - -You can customize the appearance of the Flow component using CSS. The Flow component comes with a default theme, which you can override with your own styles. - -## CSS Variables - -The Flow component uses CSS variables for theming. You can override these variables to change the appearance of the flow. Here are some of the most common variables: - -```css -.react-flow { - --xy-background-color: #f7f9fb; - --xy-node-border-default: 1px solid #ededed; - --xy-node-boxshadow-default: 0px 3.54px 4.55px 0px #00000005, - 0px 3.54px 4.55px 0px #0000000d, 0px 0.51px 1.01px 0px #0000001a; - --xy-node-border-radius-default: 8px; - --xy-handle-background-color-default: #ffffff; - --xy-handle-border-color-default: #aaaaaa; - --xy-edge-label-color-default: #505050; -} -``` - -## Custom Stylesheets - -You can add custom stylesheets to your app to override the default styles. To do this, add the `stylesheets` prop to your `rxe.App` or `rx.App` instance: - -```python -app = rxe.App( - stylesheets=[ - "/css/my-custom-styles.css", - ], -) -``` - -Then, create a file `assets/css/my-custom-styles.css` in your project and add your custom styles there. - -## Customizing Node and Edge Styles - -You can also apply custom styles to individual nodes and edges using the `style` and `className` props. - -### Using the style prop - -You can pass a style dictionary to the `style` prop of a node or edge: - -```python -node = { - "id": "1", - "position": {"x": 100, "y": 100}, - "data": {"label": "Node 1"}, - "style": {"backgroundColor": "#ffcc00"}, -} -``` - -### Using the className prop - -You can also pass a class name to the `className` prop and define the styles in your CSS file: - -```python -# In your python code -node = { - "id": "1", - "position": {"x": 100, "y": 100}, - "data": {"label": "Node 1"}, - "className": "my-custom-node", -} -``` - -```css -/* In your CSS file */ -.my-custom-node { - background-color: #ffcc00; - border: 2px solid #ff9900; - border-radius: 10px; -} -``` diff --git a/docs/enterprise/react_flow/utils.md b/docs/enterprise/react_flow/utils.md deleted file mode 100644 index 1f0138574..000000000 --- a/docs/enterprise/react_flow/utils.md +++ /dev/null @@ -1,29 +0,0 @@ -# Utility Functions - -The `rxe.flow.util` module provides utility functions for working with Flow components. - -## Path Utilities - -These functions are used to calculate the path for an edge. - -- `get_simple_bezier_path(source_x, source_y, target_x, target_y, source_position="bottom", target_position="top")`: Returns everything you need to render a simple bezier edge between two nodes. -- `get_bezier_path(source_x, source_y, target_x, target_y, source_position="bottom", target_position="top", curvature=0.5)`: Returns everything you need to render a bezier edge between two nodes. -- `get_straight_path(source_x, source_y, target_x, target_y)`: Calculates the straight line path between two points. -- `get_smooth_step_path(source_x, source_y, target_x, target_y, source_position="bottom", target_position="top", border_radius=5, center_x=None, center_y=None, offset=20, step_position=0.5)`: Returns everything you need to render a stepped path between two nodes. - -## Change Handlers - -These functions are used to apply changes to nodes and edges from the `on_nodes_change` and `on_edges_change` event handlers. - -- `apply_node_changes(nodes, changes)`: Applies changes to nodes in the flow. -- `apply_edge_changes(edges, changes)`: Applies changes to edges in the flow. - -## Edge and Connection Utilities - -- `add_edge(params, edges)`: Creates a new edge in the flow. - -## Graph Utilities - -- `get_incomers(node_id, nodes, edges)`: Returns all incoming nodes connected to the given node. -- `get_outgoers(node_id, nodes, edges)`: Returns all outgoing nodes connected to the given node. -- `get_connected_edges(nodes, edges)`: Returns all edges connected to the given nodes. diff --git a/docs/enterprise/single-port-proxy.md b/docs/enterprise/single-port-proxy.md deleted file mode 100644 index 078b9a703..000000000 --- a/docs/enterprise/single-port-proxy.md +++ /dev/null @@ -1,15 +0,0 @@ -# Single Port Proxy - -Enable single-port deployment by proxying the backend to the frontend port. - -## Configuration - -```python -import reflex_enterprise as rxe - -config = rxe.Config( - use_single_port=True, -) -``` - -This allows your application to run on a single port, which is useful for deployment scenarios where you can only expose one port. \ No newline at end of file diff --git a/docs/hosting/adding-members.md b/docs/hosting/adding-members.md deleted file mode 100644 index b6b6c07c0..000000000 --- a/docs/hosting/adding-members.md +++ /dev/null @@ -1,39 +0,0 @@ -```python exec -import reflex as rx -from pcweb.constants import REFLEX_ASSETS_CDN -from pcweb.components.image_zoom import image_zoom -``` - -# Project - -A project is a collection of applications (apps / websites). - -Every project has its own billing page that are accessible to Admins. - - - -## Adding Team Members - -To see the team members of a project click on the `Members` tab in the Cloud UI on the project page. - -If you are a User you have the ability to create, deploy and delete apps, but you do not have the power to add or delete users from that project. You must be an Admin for that. - -As an Admin you will see the an `Add user` button in the top right of the screen, as shown in the image below. Clicking on this will allow you to add a user to the project. You will need to enter the email address of the user you wish to add. - -```python eval -image_zoom(rx.image(src=f"{REFLEX_ASSETS_CDN}other/hosting_adding_team_members.webp", alt="Adding team members to Reflex Cloud")) -``` - -```python eval -rx.box(height="20px") -``` - -```md alert warning -# Currently a User must already have logged in once before they can be added to a project. -At this time a User must be logged in to be added to a project. In future there will be automatic email invites sent to add new users who have never logged in before. -``` - - -## Other project settings - -Clicking on the `Settings` tab in the Cloud UI on the project page allows a user to change the `project name`, check the `project id` and, if the project is not your default project, delete the project. \ No newline at end of file diff --git a/docs/hosting/app-management.md b/docs/hosting/app-management.md deleted file mode 100644 index 26147c61a..000000000 --- a/docs/hosting/app-management.md +++ /dev/null @@ -1,58 +0,0 @@ -```python exec -import reflex as rx -from pcweb.constants import REFLEX_ASSETS_CDN -from pcweb.components.image_zoom import image_zoom -from pcweb.pages.docs import hosting -from pcweb.pages import docs -from pcweb.styles.styles import get_code_style, cell_style -``` - -# App - -In Reflex Cloud an "app" (or "application" or "website") refers to a web application built using the Reflex framework, which can be deployed and managed within the Cloud platform. - -You can deploy an app using the `reflex deploy` command. - -There are many actions you can take in the Cloud UI to manage your app. Below are some of the most common actions you may want to take. - - -## Stopping an App - -To stop an app follow the arrow in the image below and press on the `Stop app` button. Pausing an app will stop it from running and will not be accessible to users until you resume it. In addition, this will stop you being billed for your app. - -```python eval -image_zoom(rx.image(src=f"{REFLEX_ASSETS_CDN}other/stopping_app.webp", padding_bottom="20px")) -``` - -```md alert info -# CLI Command to stop an app -`reflex cloud apps stop [OPTIONS] [APP_ID]` -``` - -## Deleting an App - -To delete an app click on the `Settings` tab in the Cloud UI on the app page. - -```python eval -image_zoom(rx.image(src=f"{REFLEX_ASSETS_CDN}other/environment_variables.webp")) -``` - -Then click on the `Danger` tab as shown below. - -```python eval -image_zoom(rx.image(src=f"{REFLEX_ASSETS_CDN}other/deleting_app.webp")) -``` - -Here there is a `Delete app` button. Pressing this button will delete the app and all of its data. This action is irreversible. - -```md alert info -# CLI Command to delete an app -`reflex cloud apps delete [OPTIONS] [APP_ID]` -``` - - -## Other app settings - -Clicking on the `Settings` tab in the Cloud UI on the app page also allows a user to change the `app name`, change the `app description` and check the `app id`. - -The other app settings also allows users to edit and add secrets (environment variables) to the app. For more information on secrets, see the [Secrets (Environment Variables)]({hosting.secrets_environment_vars.path}) page. diff --git a/docs/hosting/billing.md b/docs/hosting/billing.md deleted file mode 100644 index b7baac9a8..000000000 --- a/docs/hosting/billing.md +++ /dev/null @@ -1,32 +0,0 @@ -```python exec -import reflex as rx -from pcweb.components.image_zoom import image_zoom -from pcweb.pages.pricing.calculator import compute_table_base -from pcweb.pages.docs import hosting -``` - -## Overview - -Billing for Reflex Cloud is monthly per project. Project owners and admins are able to view and manage the billing page. - -The billing for a project is comprised of two parts - number of `seats` and `compute`. - -## Seats - -Projects on a paid plan can invite collaborators to join their project. - -Each additional collaborator is considered a `seat` and is charged on a flat monthly rate. Project owners and admins can manage permissions and roles for each seat in the settings tab on the project page. - -## Compute - -Reflex Cloud is billed on a per second basis so you only pay for when your app is being used by your end users. When your app is idle, you are not charged. - -For more information on compute pricing, please see the [compute]({hosting.compute.path}) page. - -## Manage Billing - -To manage your billing, you can go to the `Billing` tab in the Cloud UI on the project page. - -## Setting Billing Limits - -If you want to set a billing limit for your project, you can do so by going to the `Billing` tab in the Cloud UI on the project page. diff --git a/docs/hosting/compute.md b/docs/hosting/compute.md deleted file mode 100644 index ec85411ec..000000000 --- a/docs/hosting/compute.md +++ /dev/null @@ -1,230 +0,0 @@ -```python exec -import reflex as rx -from pcweb.components.image_zoom import image_zoom -from pcweb.pages.pricing.calculator import compute_table_base -``` - -## Compute Usage - -Reflex Cloud is billed on a per second basis so you only pay for when your app is being used by your end users. When your app is idle, you are not charged. - -This allows you to deploy your app on larger sizes and multiple regions without worrying about paying for idle compute. We bill on a per second basis so you only pay for the compute you use. - -By default your app stays alive for 5 minutes after the no users are connected. After this time your app will be considered idle and you will not be charged. Start up times usually take less than 1 second for you apps to come back online. - -#### Warm vs Cold Start -- Apps below `c2m2` are considered warm starts and are usually less than 1 second. -- If your app is larger than `c2m2` it will be a cold start which takes around 15 seconds. If you want to avoid this you can reserve a machine. - -## Compute Pricing Table - -```python eval -compute_table_base() -``` - -## Reserved Machines (Coming Soon) - -If you expect your apps to be continuously receiving users, you may want to reserve a machine instead of having us manage your compute. - -This will be a flat monthly rate for the machine. - -## Monitoring Usage - -To monitor your projects usage, you can go to the billing tab in the Reflex Cloud UI on the project page. - -Here you can see the current billing and usage for your project. - - -## Real Life Examples of compute charges on the paid tiers - - -```md alert -# Single Application - Single Region - -Anna, a hobbyist game developer, built a pixel art generator and hosted it on Reflex Cloud so fellow artists could use it anytime. She deployed her app in the San Francisco region, where she lives. If her users use the site for an hour a day, how much would Anna pay? - -**Facts:** - -- **Machine size:** `c1m1` (1 CPU, 1 GB Memory) - `$0.083` per hour -- **Regions:** `1` (SJC) -- **Avg usage per day per region:** `1 Hour` - -**Maths:** - -`1 region * 1 hour * 30 days = 30 compute hours` - -`30 * 0.083 = 2.49` - -(assuming a 30 day month) - -Anna's total cost for compute would be `$2.49` for the month. However, since paid users receive a `$10` credit, her compute cost is fully covered. - -**Charge for compute:** - -`$0.00 dollars` -``` - - - -```md alert -# Single Application - Multi Region - -Bob created a social media application and decided to host it on Reflex Cloud. Bob has users in Paris, London, San Jose and Sydney. Bob decided to deploy his application to all those region as well as additional one in Paris since that where Bob lives. If users use the site in each region for 30 minutes a day how much would Bob pay? - -**Facts:** - -- **Machine size:** `c1m1` (1 CPU, 1 GB Memory) - `$0.083` Cost per hour -- **Regions:** `5` (CDG x 2, LHR x 1, SJC x 1, SYD x 1) -- **Avg usage per day per region:** `0.5 Hours` - -**Maths:** - -`5 regions * 0.5 hours * 30 days = 75 compute hours` - -`75 * 0.083 = 6.23` - -(assuming a 30 day month) - -Bob would owe `$6.23` for this month. However since Bob is a paid user they receive a `$10` credit which brings Bob's bill down to `$0`. - -**Charge for compute:** - -`$0.00 dollars` -``` - - - - -```md alert -# Single Growing Application - Multi Region - -Charlie, a small startup founder, built a finance tracking app that allows users to create and share finance insights in real time. As his user base expanded across different regions, he needed a multi-region setup to reduce latency and improve performance. To ensure a smooth experience, he deployed his app on Reflex Cloud using a `c1m2` machine in four regions. - -If users access the app on average for **16 hours per week** in each region, how much would Charlie pay? - -**Facts:** -- **Machine size:** `c1m2` (1 CPU, 2 GB Memory) - `$0.157` per hour -- **Regions:** `4` -- **Avg usage per week per region:** `16 Hours` - -**Maths:** - -`4 regions * 16 hours * 4 weeks = 256 compute hours` - -`256 * 0.157 = 40.19` - -(assuming 4 weeks in a month) - -Charlie would owe `$40.19` for this month. However since Charlie is a paid user they receive a `$10` credit which brings Bob's bill down to `$30.19`. - -**Charge for compute:** - -`$30.19 dollars` - -``` - - - -```md alert -# Single Application High-Performance App - Single Region - -David, an **AI enthusiast**, developed a **real-time image enhancement tool** that allows photographers to upscale and enhance their images using machine learning. Since his app requires more processing power, he deployed it on a **`c2m2` machine**, which offers increased CPU and memory to handle the intensive AI workloads. - -With users accessing the app **2 hours per day** over a **30-day month**, how much would David pay? - -**Facts:** -- **Machine size:** `c2m2` (2 CPU, 2 GB Memory) - `$0.166` per hour -- **Regions:** `1` -- **Avg usage per day:** `2 Hours` - - -**Maths:** - -`1 region * 2 hours * 30 days = 60 compute hours` - -`60 * 0.166 = 9.96` - -(assuming a 30 day month) - -David would owe `$9.96` for this month. However since David is a paid user they receive a `$10` credit, he will not be charged for compute for this month. - -**Charge for compute:** - -`$0.00 dollars` - -``` - - -```md alert -# Single Fast Scaling App - Multiple Regions - -Emily, a **productivity app developer**, built a **real-time team collaboration tool** that helps remote teams manage tasks and communicate efficiently. With users spread across multiple locations, she needed **low-latency performance** to ensure a seamless experience. To achieve this, Emily deployed her app using a `c1m1` machine in **three regions**. - -With users actively using the app **6 hours per day in each region** over a **30-day month**, how much would Emily pay? - - -**Facts:** -- **Machine size:** `c1m1` (1 CPU, 1 GB Memory) - `$0.083` per hour -- **Regions:** `3` -- **Avg usage per day per region:** `6 Hours` - - -**Maths:** - -`3 regions * 6 hours * 30 days = 540 compute hours` - -`540 * 0.083 = 44.82` - -(assuming a 30 day month) - -Emily would owe `$44.82` for this month. However since Emily is a paid user they receive a `$10` credit which brings Emily's bill down to `$34.82`. - -**Charge for compute:** - -`$34.82 dollars` - -``` - - -```md alert -# Multiple Apps - Multiple Regions - -Fred, a **freelance developer**, built a **portfolio of web applications** that cater to different clients across the globe. He has built **5 apps** where **4 apps** have a small amount of traffic with an average of **0.5 hours a day** and **1 app** that has a high amount of traffic with an average of **6 hours** a day. He has deployed the 4 small traffic apps on a `c1m1` machine in **1 region** each and the high traffic app on a `c1m1` machine in **2 regions**. How much would Fred pay? - - -**Facts for 4 small traffic apps:** -- **Machine size:** `c1m1` (1 CPU, 1 GB Memory) - `$0.083` per hour -- **Regions:** `1` -- **Avg usage per day per region:** `0.5 Hours` - - -**Facts for 1 large traffic app:** -- **Machine size:** `c1m1` (1 CPU, 1 GB Memory) - `$0.083` per hour -- **Regions:** `2` -- **Avg usage per day per region:** `6 Hours` - - -**Maths:** - -4 small traffic apps: - -`4 apps * 1 region * 0.5 hours * 30 days = 60 compute hours` - -1 large traffic apps: - -`2 regions * 6 hours * 30 days = 360 compute hours` - -Total compute hours = `60 + 360 = 420 compute hours` - -`420 * 0.083 = 34.86` - -(assuming a 30 day month) - -Fred would owe `$34.86` for this month. However since Fred is a paid user they receive a `$10` credit which brings Fred's bill down to `$24.86`. - -**Charge for compute:** - -`$24.82 dollars` -``` - -One thing that is important to note is that in the hypothetical example where you have `50 people` using your app `continuously for 24 hours` or if you have `1 person` using your app `continuously for 24 hours`, you `will be charged the same amount` as the charge is based on the amount of time your app up and not the number of users using your app. In `both these examples` your `app is up for 24 hours` and therefore you will be `charged for 24 hours of compute`. \ No newline at end of file diff --git a/docs/hosting/config_file.md b/docs/hosting/config_file.md deleted file mode 100644 index 4d3685ca1..000000000 --- a/docs/hosting/config_file.md +++ /dev/null @@ -1,181 +0,0 @@ -```python exec -import reflex as rx -from pcweb.components.image_zoom import image_zoom -from pcweb import constants -from pcweb.pages.docs import hosting -from pcweb.pages import docs -from pcweb.styles.styles import get_code_style, cell_style -``` - -## What is reflex cloud config? - -The following command: - -```bash -reflex cloud config -``` - -generates a `cloud.yml` configuration file used to deploy your Reflex app to the Reflex cloud platform. This file tells Reflex how and where to run your app in the cloud. - -## Configuration File Structure - -The `cloud.yml` file uses YAML format and supports the following structure. **All fields are optional** and will use sensible defaults if not specified: - -```yaml -# Basic deployment settings -name: my-app-prod # Optional: defaults to project folder name -description: 'Production deployment' # Optional: empty by default -projectname: my-client-project # Optional: defaults to personal project - -# Infrastructure settings -regions: # Optional: defaults to sjc: 1 - sjc: 1 # San Jose (# of machines) - lhr: 2 # London (# of machines) -vmtype: c2m2 # Optional: defaults to c1m1 - -# Custom domain and environment -hostname: myapp # Optional: myapp.reflex.dev -envfile: .env.production # Optional: defaults to .env - -# Additional dependencies -packages: # Optional: empty by default - - procps -``` - -## Configuration Options Reference - -```python demo-only -rx.table.root( - rx.table.header( - rx.table.row( - rx.table.column_header_cell(rx.text("Option", size="1", weight="bold", color=rx.color("slate", 11))), - rx.table.column_header_cell(rx.text("Type", size="1", weight="bold", color=rx.color("slate", 11))), - rx.table.column_header_cell(rx.text("Default", size="1", weight="bold", color=rx.color("slate", 11))), - rx.table.column_header_cell(rx.text("Description", size="1", weight="bold", color=rx.color("slate", 11))), - align="center" - ) - ), - rx.table.body(*[ - rx.table.row( - rx.table.cell(rx.text(option, class_name="text-sm")), - rx.table.cell(rx.text(type_, class_name="text-sm")), - rx.table.cell(rx.text(default, class_name="text-sm")), - rx.table.cell(rx.link(description, href=link, class_name="text-sm") if link else rx.text(description, size="1", weight="regular")), - align="center" - ) for option, type_, default, description, link in [ - ("name", "string", "folder name", "Deployment identifier in dashboard", None), - ("description", "string", "empty", "Description of deployment", None), - ("regions", "object", "sjc: 1", "Region deployment mapping", "/docs/hosting/regions"), - ("vmtype", "string", "c1m1", "Virtual machine specifications", "/docs/hosting/machine-types"), - ("hostname", "string", "null", "Custom subdomain", None), - ("envfile", "string", ".env", "Environment variables file path", "/docs/hosting/secrets-environment-vars"), - ("project", "uuid", "null", "Project uuid", None), - ("projectname", "string", "null", "Project name", None), - ("packages", "array", "empty", "Additional system packages", None), - ("include_db", "boolean", "false", "Include local sqlite", None), - ("strategy", "string", "auto", "Deployment strategy", None) - ] - ]), - variant="ghost", - size="2", - width="100%", - max_width="800px", -) -``` - -## Configuration Options - -For details of specific sections click the links in the table. - -### Projects - -Organize deployments using projects: - -```yaml -projectname: client-alpha # Groups related deployments -``` - -You can also specify a project uuid instead of name: -```yaml -project: 12345678-1234-1234-1234-1234567890ab -``` - -You can go to the homepage of the project in the reflex cloud dashboard to find your project uuid in the url `{constants.REFLEX_CLOUD_URL.rstrip("/")}/project/uuid` - -### Apt Packages - -Install additional system packages your application requires. Package names are based on the apt package manager: - -```yaml -packages: - - procps=2.0.32-1 # Version pinning is optional - - imagemagick - - ffmpeg -``` - -### Include SQLite - -Include local sqlite database: - -```yaml -include_db: true -``` - -This is not persistent and will be lost on restart. It is recommended to use a database service instead. - -### Strategy - -Deployment strategy: -Available strategies: -- `immediate`: [Default] Deploy immediately -- `rolling`: Deploy in a rolling manner -- `bluegreen`: Deploy in a blue-green manner -- `canary`: Deploy in a canary manner, boot as single machine verify its health and then restart the rest. - -```yaml -strategy: immediate -``` - -## Multi-Environment Setup - -**Development (`cloud-dev.yml`):** -```yaml -name: myapp-dev -description: 'Development environment' -vmtype: c1m1 -envfile: .env.development -``` - -**Staging (`cloud-staging.yml`):** -```yaml -name: myapp-staging -description: 'Staging environment' -regions: - sjc: 1 -vmtype: c2m2 -envfile: .env.staging -``` - -**Production (`cloud-prod.yml`):** -```yaml -name: myapp-production -description: 'Production environment' -regions: - sjc: 2 - lhr: 1 -vmtype: c4m4 -hostname: myapp -envfile: .env.production -``` - -Deploy with specific configuration files: - -```bash -# Use default cloud.yml -reflex deploy - -# Use specific configuration file -reflex deploy --config cloud-prod.yml -reflex deploy --config cloud-staging.yml -``` - diff --git a/docs/hosting/custom-domains.md b/docs/hosting/custom-domains.md deleted file mode 100644 index b27451b34..000000000 --- a/docs/hosting/custom-domains.md +++ /dev/null @@ -1,75 +0,0 @@ -```python exec -import reflex as rx -from pcweb.constants import REFLEX_ASSETS_CDN -from pcweb.components.image_zoom import image_zoom -``` - -# Custom Domains - - -With the Enterprise tier of Reflex Cloud you can use your own custom domain to host your app. - -## Prerequisites - -You must purchase a domain from a domain registrar such as GoDaddy, Cloudflare, Namecheap, or AWS. - -For this tutorial we will use GoDaddy and the example domain `tomgotsman.us`. - - -## Steps - -Once you have purchased your domain, you can add it to your Reflex Cloud app by following these steps: - -1 - Ensure you have deployed your app to Reflex Cloud. - -2 - Once your app is deployed click the `Custom Domain` tab and add your custom domain to the input field and press the Add domain button. You should now see a page like below: - -```python eval -image_zoom(rx.image(src=f"{REFLEX_ASSETS_CDN}other/custom-domains-DNS-inputs.webp")) -``` - -```python eval -rx.box(height="20px") -``` - -3 - On the domain registrar's website, navigate to the DNS settings for your domain. It should look something like the image below: - -```python eval -image_zoom(rx.image(src=f"{REFLEX_ASSETS_CDN}other/custom-domains-DNS-before.webp")) -``` - -```python eval -rx.box(height="20px") -``` - -4 - Add all four of the DNS records provided by Reflex Cloud to your domain registrar's DNS settings. If there is already an A name record, delete it and replace it with the one provided by Reflex Cloud. Your DNS settings should look like the image below: - -```python eval -image_zoom(rx.image(src=f"{REFLEX_ASSETS_CDN}other/custom-domains-DNS-after.webp")) -``` - -```md alert warning -# It may alert you that this record will resolve on ######.tomgotsman.us.tomgotsman.us. -If this happens ensure that you select to only have the record resolve on ######.tomgotsman.us. -``` - -```md alert warning -# Your domain provider may not support an Apex CNAME record, in this case just use an A record. -![Image showing failed CNAME record](/custom-domains-CNAME-fail.png) -``` - -```python eval -rx.box(height="20px") -``` - -5 - Once you have added the DNS records, refresh the page on the Reflex Cloud page (it may take a few minutes to a few hours to update successfully). If the records are correct, you should see a success message like the one below: - -```python eval -image_zoom(rx.image(src=f"{REFLEX_ASSETS_CDN}other/custom-domains-success.webp")) -``` - -```python eval -rx.box(height="20px") -``` - -6 - Now redeploy your app using the `reflex deploy` command and your app should now be live on your custom domain! \ No newline at end of file diff --git a/docs/hosting/databricks.md b/docs/hosting/databricks.md deleted file mode 100644 index 67a4967d1..000000000 --- a/docs/hosting/databricks.md +++ /dev/null @@ -1,209 +0,0 @@ -```python exec -import reflex as rx -from pcweb import constants -from pcweb.styles.styles import get_code_style, cell_style - -``` - -# Deploy Reflex to Databricks - -This guide walks you through deploying a Reflex web application on Databricks using the Apps platform. - -## Prerequisites - -- Databricks workspace with Unity Catalog enabled -- GitHub repository containing your Reflex application -- Reflex Enterprise license (for single-port deployment) - -## Step 1: Connect Your Repository - -1. **Link GitHub Repository** - - Navigate to your Databricks workspace - - Go to your user directory - - Click **Create** → **Git folder** - - Paste the URL of your GitHub repository containing the Reflex application - -## Step 2: Configure Application Settings - -### Create Configuration File - -Create a new file called `app.yaml` directly in Databricks (not in GitHub): - -```yaml -command: [ - "reflex", - "run", - "--env", - "prod", - "--backend-port", - "$DATABRICKS_APP_PORT" -] - -env: - - name: "HOME" - value: "/tmp/reflex" - - name: "REFLEX_ACCESS_TOKEN" - value: "your-token-here" - - name: "DATABRICKS_WAREHOUSE_ID" - value: "your-sql-warehouse-id" - - name: "DATABRICKS_CATALOG" - value: "your-catalog-name" - - name: "DATABRICKS_SCHEMA" - value: "your-schema-name" - - name: "REFLEX_SHOW_BUILT_WITH_REFLEX" - value: 0 -``` - -### Obtain Required Tokens - -1. **Reflex Access Token** - - Visit [Reflex Cloud Tokens]({constants.REFLEX_CLOUD_URL.rstrip("/")}/tokens/) - - Navigate to Account Settings → Tokens - - Create a new token and copy the value - - Replace `your-token-here` in the configuration -2. **Databricks Resources** - - Update `DATABRICKS_WAREHOUSE_ID` with your SQL warehouse ID - - Update `DATABRICKS_CATALOG` with your target catalog name - - Update `DATABRICKS_SCHEMA` with your target schema name - -## Step 3: Enable Single-Port Deployment - -Update your Reflex application for Databricks compatibility: - -### Update rxconfig.py - -```python -import reflex as rx -import reflex_enterprise as rxe - -rxe.Config(app_name="app", use_single_port=True) -``` - -### Update Application Entry Point - -Modify your main application file where you define `rx.App`: - -```python -import reflex_enterprise as rxe - -app = rxe.App( - # your app configuration -) -``` - -```md alert info -# Also add `reflex-enterprise` and `asgiproxy` to your `requirements.txt` file. -``` - -## Step 4: Create Databricks App - -1. **Navigate to Apps** - - Go to **Compute** → **Apps** - - Click **Create App** -2. **Configure Application** - - Select **Custom App** - - Configure SQL warehouse for your application - -## Step 5: Set Permissions - -If you are using the `samples` Catalog then you can skip the permissions section. - -### Catalog Permissions - -1. Navigate to **Catalog** → Select your target catalog -2. Go to **Permissions** -3. Add the app's service principal user -4. Grant the following permissions: - - **USE CATALOG** - - **USE SCHEMA** - -### Schema Permissions - -1. Navigate to the specific schema -2. Go to **Permissions** -3. Grant the following permissions: - - **USE SCHEMA** - - **EXECUTE** - - **SELECT** - - **READ VOLUME** (if required) - -## Step 6: Deploy Application - -1. **Initiate Deployment** - - Click **Deploy** in the Apps interface - - When prompted for the code path, provide your Git folder path or select your repository folder -2. **Monitor Deployment** - - The deployment process will begin automatically - - Monitor logs for any configuration issues - -## Updating Your Application - -To deploy updates from your GitHub repository: - -1. **Pull Latest Changes** - - In the deployment interface, click **Deployment Source** - - Select **main** branch - - Click **Pull** to fetch the latest changes from GitHub -2. **Redeploy** - - Click **Deploy** again to apply the updates - -## Configuration Reference - -```python eval -rx.table.root( - rx.table.header( - rx.table.row( - rx.table.column_header_cell("Environment Variable"), - rx.table.column_header_cell("Description"), - rx.table.column_header_cell("Example"), - ), - ), - rx.table.body( - rx.table.row( - rx.table.cell(rx.code("HOME")), - rx.table.cell("Application home directory"), - rx.table.cell(rx.code("/tmp/reflex")), - ), - rx.table.row( - rx.table.cell(rx.code("REFLEX_ACCESS_TOKEN")), - rx.table.cell("Authentication for Reflex Cloud"), - rx.table.cell(rx.code("rx_token_...")), - ), - rx.table.row( - rx.table.cell(rx.code("DATABRICKS_WAREHOUSE_ID")), - rx.table.cell("SQL warehouse identifier"), - rx.table.cell("Auto-assigned"), - ), - rx.table.row( - rx.table.cell(rx.code("DATABRICKS_CATALOG")), - rx.table.cell("Target catalog name"), - rx.table.cell(rx.code("main")), - ), - rx.table.row( - rx.table.cell(rx.code("DATABRICKS_SCHEMA")), - rx.table.cell("Target schema name"), - rx.table.cell(rx.code("default")), - ), - rx.table.row( - rx.table.cell(rx.code("REFLEX_SHOW_BUILT_WITH_REFLEX")), - rx.table.cell("Show Reflex branding (Enterprise only)"), - rx.table.cell([rx.code("0"), " or ", rx.code("1")]), - ), - ), - variant="surface", - margin_y="1em", -) -``` - -## Troubleshooting - -- **Permission Errors**: Verify that all catalog and schema permissions are correctly set -- **Port Issues**: Ensure you're using `$DATABRICKS_APP_PORT` and single-port configuration -- **Token Issues**: Verify your Reflex access token is valid and properly configured -- **Deployment Failures**: Check the deployment logs for specific error messages - -## Notes - -- Single-port deployment requires Reflex Enterprise -- Configuration must be created directly in Databricks, not pushed from GitHub -- Updates require manual pulling from the deployment interface diff --git a/docs/hosting/deploy-quick-start.md b/docs/hosting/deploy-quick-start.md deleted file mode 100644 index c2376ca61..000000000 --- a/docs/hosting/deploy-quick-start.md +++ /dev/null @@ -1,90 +0,0 @@ -# Reflex Cloud - Quick Start - -```python exec -import reflex as rx -from pcweb.constants import REFLEX_ASSETS_CDN -from pcweb.components.image_zoom import image_zoom -from pcweb.pages import docs -``` - -So far, we have been running our apps locally on our own machines. -But what if we want to share our apps with the world? This is where -the hosting service comes in. - -## Quick Start - -Reflex’s hosting service makes it easy to deploy your apps without worrying about configuring the infrastructure. - -### Prerequisites - -1. Hosting service requires `reflex>=0.6.6`. -2. This tutorial assumes you have successfully `reflex init` and `reflex run` your app. -3. Also make sure you have a `requirements.txt` file at the top level app directory that contains all your python dependencies! (To create a `requirements.txt` file, run `pip freeze > requirements.txt`.) - - -### Authentication - -First run the command below to login / signup to your Reflex Cloud account: (command line) - -```bash -reflex login -``` - -You will be redirected to your browser where you can authenticate through Github or Gmail. - -### Web UI - -Once you are at this URL and you have successfully authenticated, click on the one project you have in your workspace. You should get a screen like this: - -```python eval -image_zoom(rx.image(src=f"{REFLEX_ASSETS_CDN}other/cloud_project_page.webp", alt="Reflex Cloud Dashboard")) -``` - -This screen shows the login command and the deploy command. As we are already logged in, we can skip the login command. - -### Deployment - -Now you can start deploying your app. - -In your cloud UI copy the `reflex deploy` command similar to the one shown below. - -```bash -reflex deploy --project 2a432b8f-2605-4753-####-####0cd1#### -``` - -In your project directory (where you would normally run `reflex run`) paste this command. - -The command is by default interactive. It asks you a few questions for information required for the deployment. - - -1. The first question will compare your `requirements.txt` to your python environment and if they are different then it will ask you if you want to update your `requirements.txt` or to continue with the current one. If they are identical this question will not appear. To create a `requirements.txt` file, run `pip freeze > requirements.txt`. -2. The second question will search for a deployed app with the name of your current app, if it does not find one then it will ask if you wish to proceed in deploying your new app. -3. The third question is optional and will ask you for an app description. - - -That’s it! You should receive some feedback on the progress of your deployment and in a few minutes your app should be up. 🎉 - -For detailed information about the deploy command and its options, see the [Deploy API Reference]({docs.cloud.deploy.path}) and the [CLI Reference](https://reflex.dev/docs/api-reference/cli/). - - -```md alert info -# Once your code is uploaded, the hosting service will start the deployment. After a complete upload, exiting from the command **does not** affect the deployment process. The command prints a message when you can safely close it without affecting the deployment. -``` - -If you go back to the Cloud UI you should be able to see your deployed app and other useful app information. - - -```md alert info -# Setup a Cloud Config File -To create a `config.yml` file for your app to set your app configuration check out the [Cloud Config Docs]({docs.hosting.config_file.path}). -``` - -```md alert info -# Moving around the Cloud UI -To go back, i.e. from an app to a project or from a project to your list of projects you just click the `REFLEX logo` in the top left corner of the page. -``` - -```md alert info -# All flag values are saved between runs -All your flag values, i.e. environment variables or regions or tokens, are saved between runs. This means that if you run a command and you pass a flag value, the next time you run the same command the flag value will be the same as the last time you ran it. This means you should only set the flag values again if you want to change them. -``` diff --git a/docs/hosting/deploy-with-github-actions.md b/docs/hosting/deploy-with-github-actions.md deleted file mode 100644 index 55ff68ad7..000000000 --- a/docs/hosting/deploy-with-github-actions.md +++ /dev/null @@ -1,118 +0,0 @@ -```python exec -from pcweb.pages import docs -import reflex as rx -from pcweb.styles.styles import get_code_style, cell_style - -github_actions_configs = [ - { - "name": "auth_token", - "description": "Reflex authentication token stored in GitHub Secrets.", - "required": True, - "default": "N/A" - }, - { - "name": "project_id", - "description": "The ID of the project you want to deploy to.", - "required": True, - "default": "N/A" - }, - { - "name": "app_directory", - "description": "The directory containing your Reflex app.", - "required": False, - "default": ". (root)" - }, - { - "name": "extra_args", - "description": "Additional arguments to pass to the `reflex deploy` command.", - "required": False, - "default": "N/A" - }, - { - "name": "python_version", - "description": "The Python version to use for the deployment environment.", - "required": False, - "default": "3.12" - } -] -``` - -# Deploy with Github Actions - -This GitHub Action simplifies the deployment of Reflex applications to Reflex Cloud. It handles setting up the environment, installing the Reflex CLI, and deploying your app with minimal configuration. - -```md alert info -# This action requires `reflex>=0.6.6` -``` - -**Features:** -- Deploy Reflex apps directly from your GitHub repository to Reflex Cloud. -- Supports subdirectory-based app structures. -- Securely uses authentication tokens via GitHub Secrets. - -## Usage -### Add the Action to Your Workflow -Create a `.github/workflows/deploy.yml` file in your repository and add the following: - -```yaml -name: Deploy Reflex App - -on: - push: - branches: - - main - -jobs: - deploy: - runs-on: ubuntu-latest - steps: - - name: Deploy to Reflex Cloud - uses: reflex-dev/reflex-deploy-action@v1 - with: - auth_token: ${\{ secrets.REFLEX_PROJECT_ID }} - project_id: ${\{ secrets.REFLEX_PROJECT_ID }} - app_directory: "my-app-folder" # Optional, defaults to root - extra_args: "--env THIRD_PARTY_APIKEY=***" # Optional - python_version: "3.12" # Optional -``` - -### Set Up Your Secrets -Store your Reflex authentication token securely in your repository's secrets: - - -1. Go to your GitHub repository. -2. Navigate to Settings > Secrets and variables > Actions > New repository secret. -3. Create new secrets for `REFLEX_AUTH_TOKEN` and `REFLEX_PROJECT_ID`. - -(Create a `REFLEX_AUTH_TOKEN` in the tokens tab of your UI, check out these [docs]({docs.hosting.tokens.path}#tokens). - -The `REFLEX_PROJECT_ID` can be found in the UI when you click on the How to deploy button on the top right when inside a project and copy the ID after the `--project` flag.) - - - -### Inputs - -```python eval -rx.table.root( - rx.table.header( - rx.table.row( - rx.table.column_header_cell("Name"), - rx.table.column_header_cell("Description"), - rx.table.column_header_cell("required"), - rx.table.column_header_cell("Default"), - ), - ), - rx.table.body( - *[ - rx.table.row( - rx.table.cell(rx.code(github_config["name"], style=get_code_style("violet"))), - rx.table.cell(github_config["description"], style=cell_style), - rx.table.cell(rx.code(github_config["required"], style=get_code_style("violet"))), - rx.table.cell(rx.code(github_config["default"], style=get_code_style("violet")), min_width="100px"), - ) - for github_config in github_actions_configs - ] - ), - variant="surface", -) -``` diff --git a/docs/hosting/logs.md b/docs/hosting/logs.md deleted file mode 100644 index 1c3cd0ef1..000000000 --- a/docs/hosting/logs.md +++ /dev/null @@ -1,46 +0,0 @@ -```python exec -import reflex as rx -from pcweb.constants import REFLEX_ASSETS_CDN -from pcweb.components.image_zoom import image_zoom -from pcweb.pages.docs import hosting -from pcweb.pages import docs -from pcweb.styles.styles import get_code_style, cell_style -``` - -## View Logs - -To view the app logs follow the arrow in the image below and press on the `Logs` dropdown. - -```python eval -image_zoom(rx.image(src=f"{REFLEX_ASSETS_CDN}other/view_logs.webp", padding_bottom="20px")) -``` - -```md alert info -# CLI Command to view logs -`reflex cloud apps logs [OPTIONS] [APP_ID]` -``` - -## View Deployment Logs and Deployment History - -To view the deployment history follow the arrow in the image below and press on the `Deployments`. - -```python eval -image_zoom(rx.image(src=f"{REFLEX_ASSETS_CDN}other/view_deployment_logs.webp")) -``` - -This brings you to the page below where you can see the deployment history of your app. Click on deployment you wish to explore further. - -```python eval -image_zoom(rx.image(src=f"{REFLEX_ASSETS_CDN}other/view_deployment_logs_2.webp", padding_bottom="20px")) -``` - -```md alert info -# CLI Command to view deployment history -`reflex cloud apps history [OPTIONS] [APP_ID]` -``` - -This brings you to the page below where you can view the deployment logs of your app by clicking the `Build logs` dropdown. - -```python eval -image_zoom(rx.image(src=f"{REFLEX_ASSETS_CDN}other/view_deployment_logs_3.webp")) -``` \ No newline at end of file diff --git a/docs/hosting/machine-types.md b/docs/hosting/machine-types.md deleted file mode 100644 index fd861550f..000000000 --- a/docs/hosting/machine-types.md +++ /dev/null @@ -1,35 +0,0 @@ -```python exec -import reflex as rx -from pcweb.constants import REFLEX_ASSETS_CDN -from pcweb.components.image_zoom import image_zoom -from pcweb.pages.docs import hosting -from pcweb.pages import docs -from pcweb.styles.styles import get_code_style, cell_style -``` - -## Machine Types - - -To scale your app you can choose different VMTypes. VMTypes are different configurations of CPU and RAM. - -To scale your VM in the Cloud UI, click on the `Settings` tab in the Cloud UI on the app page, and then click on the `Scale` tab as shown below. Clicking on the `Change VM` button will allow you to scale your app. - -```python eval -image_zoom(rx.image(src=f"{REFLEX_ASSETS_CDN}other/scaling_vms.webp", padding_bottom="20px")) -``` - -### VMTypes in the CLI - -To get all the possible VMTypes you can run the following command: - -```bash -reflex cloud vmtypes -``` - -To set which VMType to use when deploying your app you can pass the `--vmtype` flag with the id of the VMType. For example: - -```bash -reflex deploy --project f88b1574-f101-####-####-5f########## --vmtype c2m4 -``` - -This will deploy your app with the `c2m4` VMType, giving your app 2 CPU cores and 4 GB of RAM. \ No newline at end of file diff --git a/docs/hosting/regions.md b/docs/hosting/regions.md deleted file mode 100644 index cb7c78695..000000000 --- a/docs/hosting/regions.md +++ /dev/null @@ -1,151 +0,0 @@ -```python exec -import reflex as rx -from pcweb.components.image_zoom import image_zoom -from pcweb.constants import REFLEX_ASSETS_CDN, REFLEX_CLOUD_URL -from pcweb.pages.docs import hosting -from pcweb.pages import docs -from pcweb.styles.styles import get_code_style, cell_style - - -REGIONS_DICT = { - "ams": "Amsterdam, Netherlands", - "arn": "Stockholm, Sweden", - "bom": "Mumbai, India", - "cdg": "Paris, France", - "dfw": "Dallas, Texas (US)", - "ewr": "Secaucus, NJ (US)", - "fra": "Frankfurt, Germany", - "gru": "Sao Paulo, Brazil", - "iad": "Ashburn, Virginia (US)", - "jnb": "Johannesburg, South Africa", - "lax": "Los Angeles, California (US)", - "lhr": "London, United Kingdom", - "nrt": "Tokyo, Japan", - "ord": "Chicago, Illinois (US)", - "sjc": "San Jose, California (US)", - "sin": "Singapore, Singapore", - "syd": "Sydney, Australia", - "yyz": "Toronto, Canada", -} - -COUNTRIES_CODES = { - "ams": "NL", - "arn": "SE", - "bom": "IN", - "cdg": "FR", - "dfw": "US", - "ewr": "US", - "fra": "DE", - "gru": "BR", - "iad": "US", - "jnb": "ZA", - "lax": "US", - "lhr": "GB", - "nrt": "JP", - "ord": "US", - "sjc": "US", - "sin": "SG", - "syd": "AU", - "yyz": "CA", -} - - -``` - -## Regions - -To scale your app you can choose different regions. Regions are different locations around the world where your app can be deployed. - -To scale your app to multiple regions in the Cloud UI, click on the `Settings` tab in the Cloud UI on the app page, and then click on the `Regions` tab as shown below. Clicking on the `Add new region` button will allow you to scale your app to multiple regions. - -```python eval -image_zoom(rx.image(src=f"{REFLEX_ASSETS_CDN}other/scaling_regions.webp", padding_bottom="20px")) -``` - -The table below show all the regions that can be deployed in. - -```python eval -rx.el.table( - rx.el.thead( - rx.el.tr( - rx.el.th( - rx.el.div( - "Region", - ), - class_name="px-6 py-3 text-left text-sm font-semibold text-secondary-12 text-nowrap", - ), - rx.el.th( - rx.el.div( - "Country", - ), - class_name="px-6 py-3 text-left text-sm font-semibold text-secondary-12 text-nowrap", - ), - ), - class_name="bg-slate-2", - ), - rx.el.tbody( - *[ - rx.el.tr( - rx.el.td( - rx.el.div( - region, - class_name="h-5 rounded-md border justify-start items-center inline-flex bg-slate-1 text-xs font-medium shrink-0 px-1.5 w-fit text-slate-12 border-slate-6" - ), - class_name="px-6 py-3", - ), - rx.el.td( - rx.el.div( - rx.image( - src=f"{REFLEX_CLOUD_URL.rstrip('/')}/flags/{COUNTRIES_CODES[region]}.svg", - class_name="rounded-[2px] mr-2 w-5 h-4", - ), - REGIONS_DICT[region], - class_name="flex flex-row items-center gap-2", - ), - class_name="px-6 py-3 text-sm font-medium text-slate-9" - ), - class_name="even:bg-slate-2 odd:bg-slate-1 hover:bg-secondary-3", - ) - for region in REGIONS_DICT.keys() - ], - class_name="divide-y divide-slate-4", - ), - class_name="w-full table-fixed rounded-xl overflow-hidden divide-y divide-slate-4", -) -``` - -### Selecting Regions to Deploy in the CLI - -Below is an example of how to deploy your app in several regions: - -```bash -reflex deploy --project f88b1574-f101-####-####-5f########## --region sjc --region iad -``` - -By default all apps are deloyed in `sjc` if no other regions are given. If you wish to deploy in another region or several regions you can pass the `--region` flag (`-r` also works) with the region code. Check out all the regions that we can deploy to below: - - -## Config File - -To create a `config.yml` file for your app run the command below: - -```bash -reflex cloud config -``` - -This will create a yaml file similar to the one below where you can edit the app configuration: - -```yaml -name: medo -description: '' -regions: - sjc: 1 - lhr: 2 -vmtype: c1m1 -hostname: null -envfile: .env -project: null -packages: -- procps -``` - diff --git a/docs/hosting/secrets-environment-vars.md b/docs/hosting/secrets-environment-vars.md deleted file mode 100644 index b0500acce..000000000 --- a/docs/hosting/secrets-environment-vars.md +++ /dev/null @@ -1,55 +0,0 @@ -```python exec -import reflex as rx -from pcweb.constants import REFLEX_ASSETS_CDN -from pcweb.components.image_zoom import image_zoom -``` - -# Secrets (Environment Variables) - - -## Adding Secrets through the CLI - -Below is an example of how to use an environment variable file. You can pass the `--envfile` flag with the path to the env file. For example: - -```bash -reflex deploy --project f88b1574-f101-####-####-5f########## --envfile .env -``` - -In this example the path to the file is `.env`. - - -If you prefer to pass the environment variables manually below is deployment command example: - -```bash -reflex deploy --project f88b1574-f101-####-####-5f########## --env OPENAI_API_KEY=sk-proj-vD4i9t6U############################ -``` - -They are passed after the `--env` flag as key value pairs. - -To pass multiple environment variables, you can repeat the `--env` tag. i.e. `reflex deploy --project f88b1574-f101-####-####-5f########## --env KEY1=VALUE1 --env KEY2=VALUE`. The `--envfile` flag will override any envs set manually. - - -```md alert info -# More information on Environment Variables -Environment variables are encrypted and safely stored. We recommend that backend API keys or secrets are entered as `envs`. Make sure to enter the `envs` without any quotation marks. We do not show the values of them in any CLI commands, only their names (or keys). - -You access the values of `envs` by referencing `os.environ` with their names as keys in your app's backend. For example, if you set an env `ASYNC_DB_URL`, you are able to access it by `os.environ["ASYNC_DB_URL"]`. Some Python libraries automatically look for certain environment variables. For example, `OPENAI_API_KEY` for the `openai` python client. The `boto3` client credentials can be configured by setting `AWS_ACCESS_KEY_ID`,`AWS_SECRET_ACCESS_KEY`. This information is typically available in the documentation of the Python packages you use. -``` - -## Adding Secrets through the Cloud UI - -To find the secrets tab, click on the `Settings` tab in the Cloud UI on the app page. - -```python eval -image_zoom(rx.image(src=f"{REFLEX_ASSETS_CDN}other/environment_variables.webp")) -``` - -Then click on the `Secrets` tab as shown below. - -```python eval -image_zoom(rx.image(src=f"{REFLEX_ASSETS_CDN}other/environment_variables_2.webp")) -``` - -From here you can add or edit your environment variables. You will need to restart your app for these changes to take effect. - -This functionality in the UI can be disabled by an admin of the project. \ No newline at end of file diff --git a/docs/hosting/self-hosting.md b/docs/hosting/self-hosting.md deleted file mode 100644 index 2e687a119..000000000 --- a/docs/hosting/self-hosting.md +++ /dev/null @@ -1,121 +0,0 @@ -```python exec -import reflex as rx - -from pcweb.pages.docs import getting_started -from pcweb.pages.pricing.pricing import pricing_path -``` - -# Self Hosting - -We recommend using `reflex deploy`, but if this does not fit your use case then you can also host your apps yourself. - -Clone your code to a server and install the [requirements]({getting_started.installation.path}). - -## API URL - -Edit your `rxconfig.py` file and set `api_url` to the publicly accessible IP -address or hostname of your server, with the port `:8000` at the end. Setting -this correctly is essential for the frontend to interact with the backend state. - -For example if your server is at `app.example.com`, your config would look like this: - -```python -config = rx.Config( - app_name="your_app_name", - api_url="http://app.example.com:8000", -) -``` - -It is also possible to set the environment variable `API_URL` at run time or -export time to retain the default for local development. - -## Production Mode - -Then run your app in production mode: - -```bash -reflex run --env prod -``` - -Production mode creates an optimized build of your app. By default, the static -frontend of the app (HTML, Javascript, CSS) will be exposed on port `3000` and -the backend (event handlers) will be listening on port `8000`. - -```md alert warning -# Reverse Proxy and Websockets -Because the backend uses websockets, some reverse proxy servers, like [nginx](https://nginx.org/en/docs/http/websocket.html) or [apache](https://httpd.apache.org/docs/2.4/mod/mod_proxy.html#protoupgrade), must be configured to pass the `Upgrade` header to allow backend connectivity. -``` - -## Exporting a Static Build - -Exporting a static build of the frontend allows the app to be served using a -static hosting provider, like Netlify or Github Pages. Be sure `api_url` is set -to an accessible backend URL when the frontend is exported. - -```bash -API_URL=http://app.example.com:8000 reflex export -``` - -This will create a `frontend.zip` file with your app's minified HTML, -Javascript, and CSS build that can be uploaded to your static hosting service. - -It also creates a `backend.zip` file with your app's backend python code to -upload to your server and run. - -You can export only the frontend or backend by passing in the `--frontend-only` -or `--backend-only` flags. - -It is also possible to export the components without zipping. To do -this, use the `--no-zip` parameter. This provides the frontend in the -`.web/build/client/` directory and the backend can be found in the root directory of -the project. - -## Reflex Container Service - -Another option is to run your Reflex service in a container. For this -purpose, a `Dockerfile` and additional documentation is available in the Reflex -project in the directory `docker-example`. - -For the build of the container image it is necessary to edit the `rxconfig.py` -and the add the `requirements.txt` -to your project folder. The following changes are necessary in `rxconfig.py`: - -```python -config = rx.Config( - app_name="app", - api_url="http://app.example.com:8000", -) -``` - -Notice that the `api_url` should be set to the externally accessible hostname or -IP, as the client browser must be able to connect to it directly to establish -interactivity. - -You can find the `requirements.txt` in the `docker-example` folder of the -project too. - -The project structure should looks like this: - -```bash -hello -├── .web -├── assets -├── hello -│ ├── __init__.py -│ └── hello.py -├── rxconfig.py -├── Dockerfile -└── requirements.txt -``` - -After all changes have been made, the container image can now be created as follows. - -```bash -docker build -t reflex-project:latest . -``` - -Finally, you can start your Reflex container service as follows. - -```bash -docker run -d -p 3000:3000 -p 8000:8000 --name app reflex-project:latest -``` diff --git a/docs/hosting/tokens.md b/docs/hosting/tokens.md deleted file mode 100644 index 398ad1f6d..000000000 --- a/docs/hosting/tokens.md +++ /dev/null @@ -1,22 +0,0 @@ -```python exec -import reflex as rx -from pcweb.constants import REFLEX_ASSETS_CDN -from pcweb.components.image_zoom import image_zoom -``` - -# Tokens - -A token gives someone else all the permissions you have as a User or Admin. They can run any Reflex Cloud command from the CLI as if they were you, using the `--token` flag. A common use case is for GitHub Actions (you store this token in your secrets). - -To access or create tokens, first click the avatar in the top-right corner to open the drop-down menu, then click `Account Settings`. - -```python eval -image_zoom(rx.image(src=f"{REFLEX_ASSETS_CDN}other/hosting_tokens_1.webp", alt="Adding tokens to Reflex Cloud", padding="1em 0em")) -``` - -Clicking `Account Settings` will redirect you to both the `Settings` and `Tokens` dashboards. Click the `Tokens` tab at the top to access your tokens or create new ones. - - -```python eval -image_zoom(rx.image(src=f"{REFLEX_ASSETS_CDN}other/hosting_tokens_2.webp", alt="Adding tokens to Reflex Cloud", padding="1em 0em")) -``` diff --git a/pcweb/docgen_pipeline.py b/pcweb/docgen_pipeline.py index 1ec41a4a9..0f6211f6a 100644 --- a/pcweb/docgen_pipeline.py +++ b/pcweb/docgen_pipeline.py @@ -5,7 +5,7 @@ from pathlib import Path import reflex as rx -from reflex_core.constants.colors import ColorType +from reflex_base.constants.colors import ColorType from reflex_docgen.markdown import ( Block, CodeBlock, diff --git a/pcweb/flexdown.py b/pcweb/flexdown.py index c160fb6b0..010f408ef 100644 --- a/pcweb/flexdown.py +++ b/pcweb/flexdown.py @@ -1,7 +1,7 @@ import flexdown import reflex as rx import reflex_ui as ui -from reflex_core.constants.colors import ColorType +from reflex_base.constants.colors import ColorType from pcweb.constants import REFLEX_ASSETS_CDN from pcweb.styles.colors import c_color diff --git a/pcweb/pages/docs/__init__.py b/pcweb/pages/docs/__init__.py index 76a326b0f..a4224cf08 100644 --- a/pcweb/pages/docs/__init__.py +++ b/pcweb/pages/docs/__init__.py @@ -94,7 +94,7 @@ def get_components_from_metadata(current_doc): # --------------------------------------------------------------------------- -# Local docs (ai_builder, enterprise, hosting, etc.) — processed via flexdown +# Local docs — processed via flexdown # --------------------------------------------------------------------------- flexdown_docs = [ str(doc).replace("\\", "/") for doc in flexdown.utils.get_flexdown_files("docs/") @@ -277,10 +277,11 @@ def get_component_docgen(virtual_doc: str, actual_path: str, title: str): def comp(_actual=actual_path, _virtual=virtual_doc): toc = get_docgen_toc(_actual) + doc_content = Path(_actual).read_text(encoding="utf-8") rendered = render_docgen_document( virtual_filepath=_virtual, actual_filepath=_actual ) - return (toc, rendered) + return ((toc, doc_content), rendered) return make_docpage(resolved.route, resolved.display_title, virtual_doc, comp) @@ -361,6 +362,4 @@ def register_doc(virtual_doc: str, comp): ) for name, ns in docs_ns.__dict__.items(): - # if name == "cloud": - # print(name, ns) locals()[name] = ns diff --git a/pcweb/pages/docs/enterprise.py b/pcweb/pages/docs/enterprise.py deleted file mode 100644 index 6885786bd..000000000 --- a/pcweb/pages/docs/enterprise.py +++ /dev/null @@ -1,12 +0,0 @@ -"""The enterprise documentation pages.""" - -from pcweb.templates.docpage import docpage - - -@docpage( - set_path="/docs/enterprise/overview", - t="Overview | Enterprise", -) -def overview(): - """The enterprise overview page.""" - pass diff --git a/pcweb/templates/docpage/blocks/collapsible.py b/pcweb/templates/docpage/blocks/collapsible.py index 18f08409d..2c5def593 100644 --- a/pcweb/templates/docpage/blocks/collapsible.py +++ b/pcweb/templates/docpage/blocks/collapsible.py @@ -3,7 +3,7 @@ from collections.abc import Sequence import reflex as rx -from reflex_core.constants.colors import ColorType +from reflex_base.constants.colors import ColorType def collapsible_box( diff --git a/pcweb/templates/docpage/docpage.py b/pcweb/templates/docpage/docpage.py index b383f79ee..1d1c6d378 100644 --- a/pcweb/templates/docpage/docpage.py +++ b/pcweb/templates/docpage/docpage.py @@ -768,7 +768,7 @@ def wrapper(*args, **kwargs) -> rx.Component: for level, text in toc ], id="toc-navigation", - class_name="flex flex-col gap-y-1 list-none shadow-[1.5px_0_0_0_var(--m-slate-4)_inset] dark:shadow-[1.5px_0_0_0_var(--m-slate-9)_inset] max-h-[80vh]", + class_name="flex flex-col gap-y-1 list-none shadow-[1.5px_0_0_0_var(--m-slate-4)_inset] dark:shadow-[1.5px_0_0_0_var(--m-slate-9)_inset] max-h-[60vh] overflow-y-auto", ), rx.el.div( feedback_button_toc(), diff --git a/uv.lock b/uv.lock index 7daa28f57..daac44905 100644 --- a/uv.lock +++ b/uv.lock @@ -398,14 +398,14 @@ wheels = [ [[package]] name = "click" -version = "8.3.1" +version = "8.3.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/3d/fa/656b739db8587d7b5dfa22e22ed02566950fbfbcdc20311993483657a5c0/click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a", size = 295065, upload-time = "2025-11-15T20:45:42.706Z" } +sdist = { url = "https://files.pythonhosted.org/packages/57/75/31212c6bf2503fdf920d87fee5d7a86a2e3bcf444984126f13d8e4016804/click-8.3.2.tar.gz", hash = "sha256:14162b8b3b3550a7d479eafa77dfd3c38d9dc8951f6f69c78913a8f9a7540fd5", size = 302856, upload-time = "2026-04-03T19:14:45.118Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6", size = 108274, upload-time = "2025-11-15T20:45:41.139Z" }, + { url = "https://files.pythonhosted.org/packages/e4/20/71885d8b97d4f3dde17b1fdb92dbd4908b00541c5a3379787137285f602e/click-8.3.2-py3-none-any.whl", hash = "sha256:1924d2c27c5653561cd2cae4548d1406039cb79b858b747cfea24924bbc1616d", size = 108379, upload-time = "2026-04-03T19:14:43.505Z" }, ] [[package]] @@ -1553,11 +1553,11 @@ wheels = [ [[package]] name = "narwhals" -version = "2.18.1" +version = "2.19.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/59/96/45218c2fdec4c9f22178f905086e85ef1a6d63862dcc3cd68eb60f1867f5/narwhals-2.18.1.tar.gz", hash = "sha256:652a1fcc9d432bbf114846688884c215f17eb118aa640b7419295d2f910d2a8b", size = 620578, upload-time = "2026-03-24T15:11:25.456Z" } +sdist = { url = "https://files.pythonhosted.org/packages/4e/1a/bd3317c0bdbcd9ffb710ddf5250b32898f8f2c240be99494fe137feb77a7/narwhals-2.19.0.tar.gz", hash = "sha256:14fd7040b5ff211d415a82e4827b9d04c354e213e72a6d0730205ffd72e3b7ff", size = 623698, upload-time = "2026-04-06T15:50:58.786Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/3f/c3/06490e98393dcb4d6ce2bf331a39335375c300afaef526897881fbeae6ab/narwhals-2.18.1-py3-none-any.whl", hash = "sha256:a0a8bb80205323851338888ba3a12b4f65d352362c8a94be591244faf36504ad", size = 444952, upload-time = "2026-03-24T15:11:23.801Z" }, + { url = "https://files.pythonhosted.org/packages/37/72/e61e3091e0e00fae9d3a8ef85ece9d2cd4b5966058e1f2901ce42679eebf/narwhals-2.19.0-py3-none-any.whl", hash = "sha256:1f8dfa4a33a6dbff878c3e9be4c3b455dfcaf2a9322f1357db00e4e92e95b84b", size = 446991, upload-time = "2026-04-06T15:50:57.046Z" }, ] [[package]] @@ -2428,11 +2428,11 @@ wheels = [ [[package]] name = "python-multipart" -version = "0.0.22" +version = "0.0.24" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/94/01/979e98d542a70714b0cb2b6728ed0b7c46792b695e3eaec3e20711271ca3/python_multipart-0.0.22.tar.gz", hash = "sha256:7340bef99a7e0032613f56dc36027b959fd3b30a787ed62d310e951f7c3a3a58", size = 37612, upload-time = "2026-01-25T10:15:56.219Z" } +sdist = { url = "https://files.pythonhosted.org/packages/8a/45/e23b5dc14ddb9918ae4a625379506b17b6f8fc56ca1d82db62462f59aea6/python_multipart-0.0.24.tar.gz", hash = "sha256:9574c97e1c026e00bc30340ef7c7d76739512ab4dfd428fec8c330fa6a5cc3c8", size = 37695, upload-time = "2026-04-05T20:49:13.829Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1b/d0/397f9626e711ff749a95d96b7af99b9c566a9bb5129b8e4c10fc4d100304/python_multipart-0.0.22-py3-none-any.whl", hash = "sha256:2b2cd894c83d21bf49d702499531c7bafd057d730c201782048f7945d82de155", size = 24579, upload-time = "2026-01-25T10:15:54.811Z" }, + { url = "https://files.pythonhosted.org/packages/a3/73/89930efabd4da63cea44a3f438aeb753d600123570e6d6264e763617a9ce/python_multipart-0.0.24-py3-none-any.whl", hash = "sha256:9b110a98db707df01a53c194f0af075e736a770dc5058089650d70b4a182f950", size = 24420, upload-time = "2026-04-05T20:49:12.555Z" }, ] [[package]] @@ -2529,8 +2529,8 @@ wheels = [ [[package]] name = "reflex" -version = "0.9.0a0.post18.dev0+2755a7b8" -source = { git = "https://github.com/reflex-dev/reflex?rev=main#2755a7b82bf5b8daf1323cf9d8fb52abdb3abee4" } +version = "0.9.0a1" +source = { git = "https://github.com/reflex-dev/reflex?rev=main#11102878e676d51613c2c0acfc514c6654a6676c" } dependencies = [ { name = "alembic" }, { name = "click" }, @@ -2542,6 +2542,7 @@ dependencies = [ { name = "python-multipart" }, { name = "python-socketio" }, { name = "redis" }, + { name = "reflex-base" }, { name = "reflex-components-code" }, { name = "reflex-components-core" }, { name = "reflex-components-dataeditor" }, @@ -2554,7 +2555,6 @@ dependencies = [ { name = "reflex-components-react-player" }, { name = "reflex-components-recharts" }, { name = "reflex-components-sonner" }, - { name = "reflex-core" }, { name = "reflex-hosting-cli" }, { name = "rich" }, { name = "sqlmodel" }, @@ -2563,10 +2563,22 @@ dependencies = [ { name = "wrapt" }, ] +[[package]] +name = "reflex-base" +version = "0.9.0a4" +source = { git = "https://github.com/reflex-dev/reflex?subdirectory=packages%2Freflex-base&rev=main#11102878e676d51613c2c0acfc514c6654a6676c" } +dependencies = [ + { name = "packaging" }, + { name = "platformdirs" }, + { name = "pydantic" }, + { name = "rich" }, + { name = "typing-extensions" }, +] + [[package]] name = "reflex-components-code" -version = "0.0.0.post2931.dev0+2755a7b8" -source = { git = "https://github.com/reflex-dev/reflex?subdirectory=packages%2Freflex-components-code&rev=main#2755a7b82bf5b8daf1323cf9d8fb52abdb3abee4" } +version = "0.9.0a1" +source = { git = "https://github.com/reflex-dev/reflex?subdirectory=packages%2Freflex-components-code&rev=main#11102878e676d51613c2c0acfc514c6654a6676c" } dependencies = [ { name = "reflex-components-core" }, { name = "reflex-components-lucide" }, @@ -2575,8 +2587,8 @@ dependencies = [ [[package]] name = "reflex-components-core" -version = "0.0.0.post2931.dev0+2755a7b8" -source = { git = "https://github.com/reflex-dev/reflex?subdirectory=packages%2Freflex-components-core&rev=main#2755a7b82bf5b8daf1323cf9d8fb52abdb3abee4" } +version = "0.9.0a1" +source = { git = "https://github.com/reflex-dev/reflex?subdirectory=packages%2Freflex-components-core&rev=main#11102878e676d51613c2c0acfc514c6654a6676c" } dependencies = [ { name = "python-multipart" }, { name = "reflex-components-lucide" }, @@ -2587,26 +2599,26 @@ dependencies = [ [[package]] name = "reflex-components-dataeditor" -version = "0.0.0.post2931.dev0+2755a7b8" -source = { git = "https://github.com/reflex-dev/reflex?subdirectory=packages%2Freflex-components-dataeditor&rev=main#2755a7b82bf5b8daf1323cf9d8fb52abdb3abee4" } +version = "0.9.0a1" +source = { git = "https://github.com/reflex-dev/reflex?subdirectory=packages%2Freflex-components-dataeditor&rev=main#11102878e676d51613c2c0acfc514c6654a6676c" } dependencies = [ { name = "reflex-components-core" }, ] [[package]] name = "reflex-components-gridjs" -version = "0.0.0.post2931.dev0+2755a7b8" -source = { git = "https://github.com/reflex-dev/reflex?subdirectory=packages%2Freflex-components-gridjs&rev=main#2755a7b82bf5b8daf1323cf9d8fb52abdb3abee4" } +version = "0.9.0a1" +source = { git = "https://github.com/reflex-dev/reflex?subdirectory=packages%2Freflex-components-gridjs&rev=main#11102878e676d51613c2c0acfc514c6654a6676c" } [[package]] name = "reflex-components-lucide" -version = "0.0.0.post2931.dev0+2755a7b8" -source = { git = "https://github.com/reflex-dev/reflex?subdirectory=packages%2Freflex-components-lucide&rev=main#2755a7b82bf5b8daf1323cf9d8fb52abdb3abee4" } +version = "0.9.0a1" +source = { git = "https://github.com/reflex-dev/reflex?subdirectory=packages%2Freflex-components-lucide&rev=main#11102878e676d51613c2c0acfc514c6654a6676c" } [[package]] name = "reflex-components-markdown" -version = "0.0.0.post2931.dev0+2755a7b8" -source = { git = "https://github.com/reflex-dev/reflex?subdirectory=packages%2Freflex-components-markdown&rev=main#2755a7b82bf5b8daf1323cf9d8fb52abdb3abee4" } +version = "0.9.0a1" +source = { git = "https://github.com/reflex-dev/reflex?subdirectory=packages%2Freflex-components-markdown&rev=main#11102878e676d51613c2c0acfc514c6654a6676c" } dependencies = [ { name = "reflex-components-code" }, { name = "reflex-components-core" }, @@ -2615,21 +2627,21 @@ dependencies = [ [[package]] name = "reflex-components-moment" -version = "0.0.0.post2931.dev0+2755a7b8" -source = { git = "https://github.com/reflex-dev/reflex?subdirectory=packages%2Freflex-components-moment&rev=main#2755a7b82bf5b8daf1323cf9d8fb52abdb3abee4" } +version = "0.9.0a1" +source = { git = "https://github.com/reflex-dev/reflex?subdirectory=packages%2Freflex-components-moment&rev=main#11102878e676d51613c2c0acfc514c6654a6676c" } [[package]] name = "reflex-components-plotly" -version = "0.0.0.post2931.dev0+2755a7b8" -source = { git = "https://github.com/reflex-dev/reflex?subdirectory=packages%2Freflex-components-plotly&rev=main#2755a7b82bf5b8daf1323cf9d8fb52abdb3abee4" } +version = "0.9.0a1" +source = { git = "https://github.com/reflex-dev/reflex?subdirectory=packages%2Freflex-components-plotly&rev=main#11102878e676d51613c2c0acfc514c6654a6676c" } dependencies = [ { name = "reflex-components-core" }, ] [[package]] name = "reflex-components-radix" -version = "0.0.0.post2931.dev0+2755a7b8" -source = { git = "https://github.com/reflex-dev/reflex?subdirectory=packages%2Freflex-components-radix&rev=main#2755a7b82bf5b8daf1323cf9d8fb52abdb3abee4" } +version = "0.9.0a1" +source = { git = "https://github.com/reflex-dev/reflex?subdirectory=packages%2Freflex-components-radix&rev=main#11102878e676d51613c2c0acfc514c6654a6676c" } dependencies = [ { name = "reflex-components-core" }, { name = "reflex-components-lucide" }, @@ -2637,41 +2649,29 @@ dependencies = [ [[package]] name = "reflex-components-react-player" -version = "0.0.0.post2931.dev0+2755a7b8" -source = { git = "https://github.com/reflex-dev/reflex?subdirectory=packages%2Freflex-components-react-player&rev=main#2755a7b82bf5b8daf1323cf9d8fb52abdb3abee4" } +version = "0.9.0a1" +source = { git = "https://github.com/reflex-dev/reflex?subdirectory=packages%2Freflex-components-react-player&rev=main#11102878e676d51613c2c0acfc514c6654a6676c" } dependencies = [ { name = "reflex-components-core" }, ] [[package]] name = "reflex-components-recharts" -version = "0.0.0.post2931.dev0+2755a7b8" -source = { git = "https://github.com/reflex-dev/reflex?subdirectory=packages%2Freflex-components-recharts&rev=main#2755a7b82bf5b8daf1323cf9d8fb52abdb3abee4" } +version = "0.9.0a1" +source = { git = "https://github.com/reflex-dev/reflex?subdirectory=packages%2Freflex-components-recharts&rev=main#11102878e676d51613c2c0acfc514c6654a6676c" } [[package]] name = "reflex-components-sonner" -version = "0.0.0.post2931.dev0+2755a7b8" -source = { git = "https://github.com/reflex-dev/reflex?subdirectory=packages%2Freflex-components-sonner&rev=main#2755a7b82bf5b8daf1323cf9d8fb52abdb3abee4" } +version = "0.9.0a1" +source = { git = "https://github.com/reflex-dev/reflex?subdirectory=packages%2Freflex-components-sonner&rev=main#11102878e676d51613c2c0acfc514c6654a6676c" } dependencies = [ { name = "reflex-components-lucide" }, ] -[[package]] -name = "reflex-core" -version = "0.0.0.post2931.dev0+2755a7b8" -source = { git = "https://github.com/reflex-dev/reflex?subdirectory=packages%2Freflex-core&rev=main#2755a7b82bf5b8daf1323cf9d8fb52abdb3abee4" } -dependencies = [ - { name = "packaging" }, - { name = "platformdirs" }, - { name = "pydantic" }, - { name = "rich" }, - { name = "typing-extensions" }, -] - [[package]] name = "reflex-docgen" -version = "0.0.0.post2931.dev0+2755a7b8" -source = { git = "https://github.com/reflex-dev/reflex?subdirectory=packages%2Freflex-docgen&rev=main#2755a7b82bf5b8daf1323cf9d8fb52abdb3abee4" } +version = "0.9.0a1" +source = { git = "https://github.com/reflex-dev/reflex?subdirectory=packages%2Freflex-docgen&rev=main#11102878e676d51613c2c0acfc514c6654a6676c" } dependencies = [ { name = "griffelib" }, { name = "mistletoe" }, @@ -2834,27 +2834,27 @@ wheels = [ [[package]] name = "ruff" -version = "0.15.8" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/14/b0/73cf7550861e2b4824950b8b52eebdcc5adc792a00c514406556c5b80817/ruff-0.15.8.tar.gz", hash = "sha256:995f11f63597ee362130d1d5a327a87cb6f3f5eae3094c620bcc632329a4d26e", size = 4610921, upload-time = "2026-03-26T18:39:38.675Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/4a/92/c445b0cd6da6e7ae51e954939cb69f97e008dbe750cfca89b8cedc081be7/ruff-0.15.8-py3-none-linux_armv6l.whl", hash = "sha256:cbe05adeba76d58162762d6b239c9056f1a15a55bd4b346cfd21e26cd6ad7bc7", size = 10527394, upload-time = "2026-03-26T18:39:41.566Z" }, - { url = "https://files.pythonhosted.org/packages/eb/92/f1c662784d149ad1414cae450b082cf736430c12ca78367f20f5ed569d65/ruff-0.15.8-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:d3e3d0b6ba8dca1b7ef9ab80a28e840a20070c4b62e56d675c24f366ef330570", size = 10905693, upload-time = "2026-03-26T18:39:30.364Z" }, - { url = "https://files.pythonhosted.org/packages/ca/f2/7a631a8af6d88bcef997eb1bf87cc3da158294c57044aafd3e17030613de/ruff-0.15.8-py3-none-macosx_11_0_arm64.whl", hash = "sha256:6ee3ae5c65a42f273f126686353f2e08ff29927b7b7e203b711514370d500de3", size = 10323044, upload-time = "2026-03-26T18:39:33.37Z" }, - { url = "https://files.pythonhosted.org/packages/67/18/1bf38e20914a05e72ef3b9569b1d5c70a7ef26cd188d69e9ca8ef588d5bf/ruff-0.15.8-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fdce027ada77baa448077ccc6ebb2fa9c3c62fd110d8659d601cf2f475858d94", size = 10629135, upload-time = "2026-03-26T18:39:44.142Z" }, - { url = "https://files.pythonhosted.org/packages/d2/e9/138c150ff9af60556121623d41aba18b7b57d95ac032e177b6a53789d279/ruff-0.15.8-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:12e617fc01a95e5821648a6df341d80456bd627bfab8a829f7cfc26a14a4b4a3", size = 10348041, upload-time = "2026-03-26T18:39:52.178Z" }, - { url = "https://files.pythonhosted.org/packages/02/f1/5bfb9298d9c323f842c5ddeb85f1f10ef51516ac7a34ba446c9347d898df/ruff-0.15.8-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:432701303b26416d22ba696c39f2c6f12499b89093b61360abc34bcc9bf07762", size = 11121987, upload-time = "2026-03-26T18:39:55.195Z" }, - { url = "https://files.pythonhosted.org/packages/10/11/6da2e538704e753c04e8d86b1fc55712fdbdcc266af1a1ece7a51fff0d10/ruff-0.15.8-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d910ae974b7a06a33a057cb87d2a10792a3b2b3b35e33d2699fdf63ec8f6b17a", size = 11951057, upload-time = "2026-03-26T18:39:19.18Z" }, - { url = "https://files.pythonhosted.org/packages/83/f0/c9208c5fd5101bf87002fed774ff25a96eea313d305f1e5d5744698dc314/ruff-0.15.8-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2033f963c43949d51e6fdccd3946633c6b37c484f5f98c3035f49c27395a8ab8", size = 11464613, upload-time = "2026-03-26T18:40:06.301Z" }, - { url = "https://files.pythonhosted.org/packages/f8/22/d7f2fabdba4fae9f3b570e5605d5eb4500dcb7b770d3217dca4428484b17/ruff-0.15.8-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f29b989a55572fb885b77464cf24af05500806ab4edf9a0fd8977f9759d85b1", size = 11257557, upload-time = "2026-03-26T18:39:57.972Z" }, - { url = "https://files.pythonhosted.org/packages/71/8c/382a9620038cf6906446b23ce8632ab8c0811b8f9d3e764f58bedd0c9a6f/ruff-0.15.8-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:ac51d486bf457cdc985a412fb1801b2dfd1bd8838372fc55de64b1510eff4bec", size = 11169440, upload-time = "2026-03-26T18:39:22.205Z" }, - { url = "https://files.pythonhosted.org/packages/4d/0d/0994c802a7eaaf99380085e4e40c845f8e32a562e20a38ec06174b52ef24/ruff-0.15.8-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:c9861eb959edab053c10ad62c278835ee69ca527b6dcd72b47d5c1e5648964f6", size = 10605963, upload-time = "2026-03-26T18:39:46.682Z" }, - { url = "https://files.pythonhosted.org/packages/19/aa/d624b86f5b0aad7cef6bbf9cd47a6a02dfdc4f72c92a337d724e39c9d14b/ruff-0.15.8-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:8d9a5b8ea13f26ae90838afc33f91b547e61b794865374f114f349e9036835fb", size = 10357484, upload-time = "2026-03-26T18:39:49.176Z" }, - { url = "https://files.pythonhosted.org/packages/35/c3/e0b7835d23001f7d999f3895c6b569927c4d39912286897f625736e1fd04/ruff-0.15.8-py3-none-musllinux_1_2_i686.whl", hash = "sha256:c2a33a529fb3cbc23a7124b5c6ff121e4d6228029cba374777bd7649cc8598b8", size = 10830426, upload-time = "2026-03-26T18:40:03.702Z" }, - { url = "https://files.pythonhosted.org/packages/f0/51/ab20b322f637b369383adc341d761eaaa0f0203d6b9a7421cd6e783d81b9/ruff-0.15.8-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:75e5cd06b1cf3f47a3996cfc999226b19aa92e7cce682dcd62f80d7035f98f49", size = 11345125, upload-time = "2026-03-26T18:39:27.799Z" }, - { url = "https://files.pythonhosted.org/packages/37/e6/90b2b33419f59d0f2c4c8a48a4b74b460709a557e8e0064cf33ad894f983/ruff-0.15.8-py3-none-win32.whl", hash = "sha256:bc1f0a51254ba21767bfa9a8b5013ca8149dcf38092e6a9eb704d876de94dc34", size = 10571959, upload-time = "2026-03-26T18:39:36.117Z" }, - { url = "https://files.pythonhosted.org/packages/1f/a2/ef467cb77099062317154c63f234b8a7baf7cb690b99af760c5b68b9ee7f/ruff-0.15.8-py3-none-win_amd64.whl", hash = "sha256:04f79eff02a72db209d47d665ba7ebcad609d8918a134f86cb13dd132159fc89", size = 11743893, upload-time = "2026-03-26T18:39:25.01Z" }, - { url = "https://files.pythonhosted.org/packages/15/e2/77be4fff062fa78d9b2a4dea85d14785dac5f1d0c1fb58ed52331f0ebe28/ruff-0.15.8-py3-none-win_arm64.whl", hash = "sha256:cf891fa8e3bb430c0e7fac93851a5978fc99c8fa2c053b57b118972866f8e5f2", size = 11048175, upload-time = "2026-03-26T18:40:01.06Z" }, +version = "0.15.9" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e6/97/e9f1ca355108ef7194e38c812ef40ba98c7208f47b13ad78d023caa583da/ruff-0.15.9.tar.gz", hash = "sha256:29cbb1255a9797903f6dde5ba0188c707907ff44a9006eb273b5a17bfa0739a2", size = 4617361, upload-time = "2026-04-02T18:17:20.829Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0b/1f/9cdfd0ac4b9d1e5a6cf09bedabdf0b56306ab5e333c85c87281273e7b041/ruff-0.15.9-py3-none-linux_armv6l.whl", hash = "sha256:6efbe303983441c51975c243e26dff328aca11f94b70992f35b093c2e71801e1", size = 10511206, upload-time = "2026-04-02T18:16:41.574Z" }, + { url = "https://files.pythonhosted.org/packages/3d/f6/32bfe3e9c136b35f02e489778d94384118bb80fd92c6d92e7ccd97db12ce/ruff-0.15.9-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:4965bac6ac9ea86772f4e23587746f0b7a395eccabb823eb8bfacc3fa06069f7", size = 10923307, upload-time = "2026-04-02T18:17:08.645Z" }, + { url = "https://files.pythonhosted.org/packages/ca/25/de55f52ab5535d12e7aaba1de37a84be6179fb20bddcbe71ec091b4a3243/ruff-0.15.9-py3-none-macosx_11_0_arm64.whl", hash = "sha256:eaf05aad70ca5b5a0a4b0e080df3a6b699803916d88f006efd1f5b46302daab8", size = 10316722, upload-time = "2026-04-02T18:16:44.206Z" }, + { url = "https://files.pythonhosted.org/packages/48/11/690d75f3fd6278fe55fff7c9eb429c92d207e14b25d1cae4064a32677029/ruff-0.15.9-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9439a342adb8725f32f92732e2bafb6d5246bd7a5021101166b223d312e8fc59", size = 10623674, upload-time = "2026-04-02T18:16:50.951Z" }, + { url = "https://files.pythonhosted.org/packages/bd/ec/176f6987be248fc5404199255522f57af1b4a5a1b57727e942479fec98ad/ruff-0.15.9-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9c5e6faf9d97c8edc43877c3f406f47446fc48c40e1442d58cfcdaba2acea745", size = 10351516, upload-time = "2026-04-02T18:16:57.206Z" }, + { url = "https://files.pythonhosted.org/packages/b2/fc/51cffbd2b3f240accc380171d51446a32aa2ea43a40d4a45ada67368fbd2/ruff-0.15.9-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7b34a9766aeec27a222373d0b055722900fbc0582b24f39661aa96f3fe6ad901", size = 11150202, upload-time = "2026-04-02T18:17:06.452Z" }, + { url = "https://files.pythonhosted.org/packages/d6/d4/25292a6dfc125f6b6528fe6af31f5e996e19bf73ca8e3ce6eb7fa5b95885/ruff-0.15.9-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:89dd695bc72ae76ff484ae54b7e8b0f6b50f49046e198355e44ea656e521fef9", size = 11988891, upload-time = "2026-04-02T18:17:18.575Z" }, + { url = "https://files.pythonhosted.org/packages/13/e1/1eebcb885c10e19f969dcb93d8413dfee8172578709d7ee933640f5e7147/ruff-0.15.9-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ce187224ef1de1bd225bc9a152ac7102a6171107f026e81f317e4257052916d5", size = 11480576, upload-time = "2026-04-02T18:16:52.986Z" }, + { url = "https://files.pythonhosted.org/packages/ff/6b/a1548ac378a78332a4c3dcf4a134c2475a36d2a22ddfa272acd574140b50/ruff-0.15.9-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2b0c7c341f68adb01c488c3b7d4b49aa8ea97409eae6462d860a79cf55f431b6", size = 11254525, upload-time = "2026-04-02T18:17:02.041Z" }, + { url = "https://files.pythonhosted.org/packages/42/aa/4bb3af8e61acd9b1281db2ab77e8b2c3c5e5599bf2a29d4a942f1c62b8d6/ruff-0.15.9-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:55cc15eee27dc0eebdfcb0d185a6153420efbedc15eb1d38fe5e685657b0f840", size = 11204072, upload-time = "2026-04-02T18:17:13.581Z" }, + { url = "https://files.pythonhosted.org/packages/69/48/d550dc2aa6e423ea0bcc1d0ff0699325ffe8a811e2dba156bd80750b86dc/ruff-0.15.9-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:a6537f6eed5cda688c81073d46ffdfb962a5f29ecb6f7e770b2dc920598997ed", size = 10594998, upload-time = "2026-04-02T18:16:46.369Z" }, + { url = "https://files.pythonhosted.org/packages/63/47/321167e17f5344ed5ec6b0aa2cff64efef5f9e985af8f5622cfa6536043f/ruff-0.15.9-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:6d3fcbca7388b066139c523bda744c822258ebdcfbba7d24410c3f454cc9af71", size = 10359769, upload-time = "2026-04-02T18:17:10.994Z" }, + { url = "https://files.pythonhosted.org/packages/67/5e/074f00b9785d1d2c6f8c22a21e023d0c2c1817838cfca4c8243200a1fa87/ruff-0.15.9-py3-none-musllinux_1_2_i686.whl", hash = "sha256:058d8e99e1bfe79d8a0def0b481c56059ee6716214f7e425d8e737e412d69677", size = 10850236, upload-time = "2026-04-02T18:16:48.749Z" }, + { url = "https://files.pythonhosted.org/packages/76/37/804c4135a2a2caf042925d30d5f68181bdbd4461fd0d7739da28305df593/ruff-0.15.9-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:8e1ddb11dbd61d5983fa2d7d6370ef3eb210951e443cace19594c01c72abab4c", size = 11358343, upload-time = "2026-04-02T18:16:55.068Z" }, + { url = "https://files.pythonhosted.org/packages/88/3d/1364fcde8656962782aa9ea93c92d98682b1ecec2f184e625a965ad3b4a6/ruff-0.15.9-py3-none-win32.whl", hash = "sha256:bde6ff36eaf72b700f32b7196088970bf8fdb2b917b7accd8c371bfc0fd573ec", size = 10583382, upload-time = "2026-04-02T18:17:04.261Z" }, + { url = "https://files.pythonhosted.org/packages/4c/56/5c7084299bd2cacaa07ae63a91c6f4ba66edc08bf28f356b24f6b717c799/ruff-0.15.9-py3-none-win_amd64.whl", hash = "sha256:45a70921b80e1c10cf0b734ef09421f71b5aa11d27404edc89d7e8a69505e43d", size = 11744969, upload-time = "2026-04-02T18:16:59.611Z" }, + { url = "https://files.pythonhosted.org/packages/03/36/76704c4f312257d6dbaae3c959add2a622f63fcca9d864659ce6d8d97d3d/ruff-0.15.9-py3-none-win_arm64.whl", hash = "sha256:0694e601c028fd97dc5c6ee244675bc241aeefced7ef80cd9c6935a871078f53", size = 11005870, upload-time = "2026-04-02T18:17:15.773Z" }, ] [[package]] @@ -2998,68 +2998,69 @@ wheels = [ [[package]] name = "sqlalchemy" -version = "2.0.48" +version = "2.0.49" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "greenlet", marker = "platform_machine == 'AMD64' or platform_machine == 'WIN32' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'ppc64le' or platform_machine == 'win32' or platform_machine == 'x86_64'" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/1f/73/b4a9737255583b5fa858e0bb8e116eb94b88c910164ed2ed719147bde3de/sqlalchemy-2.0.48.tar.gz", hash = "sha256:5ca74f37f3369b45e1f6b7b06afb182af1fd5dde009e4ffd831830d98cbe5fe7", size = 9886075, upload-time = "2026-03-02T15:28:51.474Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d7/6d/b8b78b5b80f3c3ab3f7fa90faa195ec3401f6d884b60221260fd4d51864c/sqlalchemy-2.0.48-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b4c575df7368b3b13e0cebf01d4679f9a28ed2ae6c1cd0b1d5beffb6b2007dc", size = 2157184, upload-time = "2026-03-02T15:38:28.161Z" }, - { url = "https://files.pythonhosted.org/packages/21/4b/4f3d4a43743ab58b95b9ddf5580a265b593d017693df9e08bd55780af5bb/sqlalchemy-2.0.48-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e83e3f959aaa1c9df95c22c528096d94848a1bc819f5d0ebf7ee3df0ca63db6c", size = 3313555, upload-time = "2026-03-02T15:58:57.21Z" }, - { url = "https://files.pythonhosted.org/packages/21/dd/3b7c53f1dbbf736fd27041aee68f8ac52226b610f914085b1652c2323442/sqlalchemy-2.0.48-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6f7b7243850edd0b8b97043f04748f31de50cf426e939def5c16bedb540698f7", size = 3313057, upload-time = "2026-03-02T15:52:29.366Z" }, - { url = "https://files.pythonhosted.org/packages/d9/cc/3e600a90ae64047f33313d7d32e5ad025417f09d2ded487e8284b5e21a15/sqlalchemy-2.0.48-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:82745b03b4043e04600a6b665cb98697c4339b24e34d74b0a2ac0a2488b6f94d", size = 3265431, upload-time = "2026-03-02T15:58:59.096Z" }, - { url = "https://files.pythonhosted.org/packages/8b/19/780138dacfe3f5024f4cf96e4005e91edf6653d53d3673be4844578faf1d/sqlalchemy-2.0.48-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e5e088bf43f6ee6fec7dbf1ef7ff7774a616c236b5c0cb3e00662dd71a56b571", size = 3287646, upload-time = "2026-03-02T15:52:31.569Z" }, - { url = "https://files.pythonhosted.org/packages/40/fd/f32ced124f01a23151f4777e4c705f3a470adc7bd241d9f36a7c941a33bf/sqlalchemy-2.0.48-cp311-cp311-win32.whl", hash = "sha256:9c7d0a77e36b5f4b01ca398482230ab792061d243d715299b44a0b55c89fe617", size = 2116956, upload-time = "2026-03-02T15:46:54.535Z" }, - { url = "https://files.pythonhosted.org/packages/58/d5/dd767277f6feef12d05651538f280277e661698f617fa4d086cce6055416/sqlalchemy-2.0.48-cp311-cp311-win_amd64.whl", hash = "sha256:583849c743e0e3c9bb7446f5b5addeacedc168d657a69b418063dfdb2d90081c", size = 2141627, upload-time = "2026-03-02T15:46:55.849Z" }, - { url = "https://files.pythonhosted.org/packages/ef/91/a42ae716f8925e9659df2da21ba941f158686856107a61cc97a95e7647a3/sqlalchemy-2.0.48-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:348174f228b99f33ca1f773e85510e08927620caa59ffe7803b37170df30332b", size = 2155737, upload-time = "2026-03-02T15:49:13.207Z" }, - { url = "https://files.pythonhosted.org/packages/b9/52/f75f516a1f3888f027c1cfb5d22d4376f4b46236f2e8669dcb0cddc60275/sqlalchemy-2.0.48-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:53667b5f668991e279d21f94ccfa6e45b4e3f4500e7591ae59a8012d0f010dcb", size = 3337020, upload-time = "2026-03-02T15:50:34.547Z" }, - { url = "https://files.pythonhosted.org/packages/37/9a/0c28b6371e0cdcb14f8f1930778cb3123acfcbd2c95bb9cf6b4a2ba0cce3/sqlalchemy-2.0.48-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:34634e196f620c7a61d18d5cf7dc841ca6daa7961aed75d532b7e58b309ac894", size = 3349983, upload-time = "2026-03-02T15:53:25.542Z" }, - { url = "https://files.pythonhosted.org/packages/1c/46/0aee8f3ff20b1dcbceb46ca2d87fcc3d48b407925a383ff668218509d132/sqlalchemy-2.0.48-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:546572a1793cc35857a2ffa1fe0e58571af1779bcc1ffa7c9fb0839885ed69a9", size = 3279690, upload-time = "2026-03-02T15:50:36.277Z" }, - { url = "https://files.pythonhosted.org/packages/ce/8c/a957bc91293b49181350bfd55e6dfc6e30b7f7d83dc6792d72043274a390/sqlalchemy-2.0.48-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:07edba08061bc277bfdc772dd2a1a43978f5a45994dd3ede26391b405c15221e", size = 3314738, upload-time = "2026-03-02T15:53:27.519Z" }, - { url = "https://files.pythonhosted.org/packages/4b/44/1d257d9f9556661e7bdc83667cc414ba210acfc110c82938cb3611eea58f/sqlalchemy-2.0.48-cp312-cp312-win32.whl", hash = "sha256:908a3fa6908716f803b86896a09a2c4dde5f5ce2bb07aacc71ffebb57986ce99", size = 2115546, upload-time = "2026-03-02T15:54:31.591Z" }, - { url = "https://files.pythonhosted.org/packages/f2/af/c3c7e1f3a2b383155a16454df62ae8c62a30dd238e42e68c24cebebbfae6/sqlalchemy-2.0.48-cp312-cp312-win_amd64.whl", hash = "sha256:68549c403f79a8e25984376480959975212a670405e3913830614432b5daa07a", size = 2142484, upload-time = "2026-03-02T15:54:34.072Z" }, - { url = "https://files.pythonhosted.org/packages/d1/c6/569dc8bf3cd375abc5907e82235923e986799f301cd79a903f784b996fca/sqlalchemy-2.0.48-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e3070c03701037aa418b55d36532ecb8f8446ed0135acb71c678dbdf12f5b6e4", size = 2152599, upload-time = "2026-03-02T15:49:14.41Z" }, - { url = "https://files.pythonhosted.org/packages/6d/ff/f4e04a4bd5a24304f38cb0d4aa2ad4c0fb34999f8b884c656535e1b2b74c/sqlalchemy-2.0.48-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2645b7d8a738763b664a12a1542c89c940daa55196e8d73e55b169cc5c99f65f", size = 3278825, upload-time = "2026-03-02T15:50:38.269Z" }, - { url = "https://files.pythonhosted.org/packages/fe/88/cb59509e4668d8001818d7355d9995be90c321313078c912420603a7cb95/sqlalchemy-2.0.48-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b19151e76620a412c2ac1c6f977ab1b9fa7ad43140178345136456d5265b32ed", size = 3295200, upload-time = "2026-03-02T15:53:29.366Z" }, - { url = "https://files.pythonhosted.org/packages/87/dc/1609a4442aefd750ea2f32629559394ec92e89ac1d621a7f462b70f736ff/sqlalchemy-2.0.48-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5b193a7e29fd9fa56e502920dca47dffe60f97c863494946bd698c6058a55658", size = 3226876, upload-time = "2026-03-02T15:50:39.802Z" }, - { url = "https://files.pythonhosted.org/packages/37/c3/6ae2ab5ea2fa989fbac4e674de01224b7a9d744becaf59bb967d62e99bed/sqlalchemy-2.0.48-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:36ac4ddc3d33e852da9cb00ffb08cea62ca05c39711dc67062ca2bb1fae35fd8", size = 3265045, upload-time = "2026-03-02T15:53:31.421Z" }, - { url = "https://files.pythonhosted.org/packages/6f/82/ea4665d1bb98c50c19666e672f21b81356bd6077c4574e3d2bbb84541f53/sqlalchemy-2.0.48-cp313-cp313-win32.whl", hash = "sha256:389b984139278f97757ea9b08993e7b9d1142912e046ab7d82b3fbaeb0209131", size = 2113700, upload-time = "2026-03-02T15:54:35.825Z" }, - { url = "https://files.pythonhosted.org/packages/b7/2b/b9040bec58c58225f073f5b0c1870defe1940835549dafec680cbd58c3c3/sqlalchemy-2.0.48-cp313-cp313-win_amd64.whl", hash = "sha256:d612c976cbc2d17edfcc4c006874b764e85e990c29ce9bd411f926bbfb02b9a2", size = 2139487, upload-time = "2026-03-02T15:54:37.079Z" }, - { url = "https://files.pythonhosted.org/packages/f4/f4/7b17bd50244b78a49d22cc63c969d71dc4de54567dc152a9b46f6fae40ce/sqlalchemy-2.0.48-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:69f5bc24904d3bc3640961cddd2523e361257ef68585d6e364166dfbe8c78fae", size = 3558851, upload-time = "2026-03-02T15:57:48.607Z" }, - { url = "https://files.pythonhosted.org/packages/20/0d/213668e9aca61d370f7d2a6449ea4ec699747fac67d4bda1bb3d129025be/sqlalchemy-2.0.48-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fd08b90d211c086181caed76931ecfa2bdfc83eea3cfccdb0f82abc6c4b876cb", size = 3525525, upload-time = "2026-03-02T16:04:38.058Z" }, - { url = "https://files.pythonhosted.org/packages/85/d7/a84edf412979e7d59c69b89a5871f90a49228360594680e667cb2c46a828/sqlalchemy-2.0.48-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:1ccd42229aaac2df431562117ac7e667d702e8e44afdb6cf0e50fa3f18160f0b", size = 3466611, upload-time = "2026-03-02T15:57:50.759Z" }, - { url = "https://files.pythonhosted.org/packages/86/55/42404ce5770f6be26a2b0607e7866c31b9a4176c819e9a7a5e0a055770be/sqlalchemy-2.0.48-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f0dcbc588cd5b725162c076eb9119342f6579c7f7f55057bb7e3c6ff27e13121", size = 3475812, upload-time = "2026-03-02T16:04:40.092Z" }, - { url = "https://files.pythonhosted.org/packages/ae/ae/29b87775fadc43e627cf582fe3bda4d02e300f6b8f2747c764950d13784c/sqlalchemy-2.0.48-cp313-cp313t-win32.whl", hash = "sha256:9764014ef5e58aab76220c5664abb5d47d5bc858d9debf821e55cfdd0f128485", size = 2141335, upload-time = "2026-03-02T15:52:51.518Z" }, - { url = "https://files.pythonhosted.org/packages/91/44/f39d063c90f2443e5b46ec4819abd3d8de653893aae92df42a5c4f5843de/sqlalchemy-2.0.48-cp313-cp313t-win_amd64.whl", hash = "sha256:e2f35b4cccd9ed286ad62e0a3c3ac21e06c02abc60e20aa51a3e305a30f5fa79", size = 2173095, upload-time = "2026-03-02T15:52:52.79Z" }, - { url = "https://files.pythonhosted.org/packages/f7/b3/f437eaa1cf028bb3c927172c7272366393e73ccd104dcf5b6963f4ab5318/sqlalchemy-2.0.48-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:e2d0d88686e3d35a76f3e15a34e8c12d73fc94c1dea1cd55782e695cc14086dd", size = 2154401, upload-time = "2026-03-02T15:49:17.24Z" }, - { url = "https://files.pythonhosted.org/packages/6c/1c/b3abdf0f402aa3f60f0df6ea53d92a162b458fca2321d8f1f00278506402/sqlalchemy-2.0.48-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:49b7bddc1eebf011ea5ab722fdbe67a401caa34a350d278cc7733c0e88fecb1f", size = 3274528, upload-time = "2026-03-02T15:50:41.489Z" }, - { url = "https://files.pythonhosted.org/packages/f2/5e/327428a034407651a048f5e624361adf3f9fbac9d0fa98e981e9c6ff2f5e/sqlalchemy-2.0.48-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:426c5ca86415d9b8945c7073597e10de9644802e2ff502b8e1f11a7a2642856b", size = 3279523, upload-time = "2026-03-02T15:53:32.962Z" }, - { url = "https://files.pythonhosted.org/packages/2a/ca/ece73c81a918add0965b76b868b7b5359e068380b90ef1656ee995940c02/sqlalchemy-2.0.48-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:288937433bd44e3990e7da2402fabc44a3c6c25d3704da066b85b89a85474ae0", size = 3224312, upload-time = "2026-03-02T15:50:42.996Z" }, - { url = "https://files.pythonhosted.org/packages/88/11/fbaf1ae91fa4ee43f4fe79661cead6358644824419c26adb004941bdce7c/sqlalchemy-2.0.48-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:8183dc57ae7d9edc1346e007e840a9f3d6aa7b7f165203a99e16f447150140d2", size = 3246304, upload-time = "2026-03-02T15:53:34.937Z" }, - { url = "https://files.pythonhosted.org/packages/fa/a8/5fb0deb13930b4f2f698c5541ae076c18981173e27dd00376dbaea7a9c82/sqlalchemy-2.0.48-cp314-cp314-win32.whl", hash = "sha256:1182437cb2d97988cfea04cf6cdc0b0bb9c74f4d56ec3d08b81e23d621a28cc6", size = 2116565, upload-time = "2026-03-02T15:54:38.321Z" }, - { url = "https://files.pythonhosted.org/packages/95/7e/e83615cb63f80047f18e61e31e8e32257d39458426c23006deeaf48f463b/sqlalchemy-2.0.48-cp314-cp314-win_amd64.whl", hash = "sha256:144921da96c08feb9e2b052c5c5c1d0d151a292c6135623c6b2c041f2a45f9e0", size = 2142205, upload-time = "2026-03-02T15:54:39.831Z" }, - { url = "https://files.pythonhosted.org/packages/83/e3/69d8711b3f2c5135e9cde5f063bc1605860f0b2c53086d40c04017eb1f77/sqlalchemy-2.0.48-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5aee45fd2c6c0f2b9cdddf48c48535e7471e42d6fb81adfde801da0bd5b93241", size = 3563519, upload-time = "2026-03-02T15:57:52.387Z" }, - { url = "https://files.pythonhosted.org/packages/f8/4f/a7cce98facca73c149ea4578981594aaa5fd841e956834931de503359336/sqlalchemy-2.0.48-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7cddca31edf8b0653090cbb54562ca027c421c58ddde2c0685f49ff56a1690e0", size = 3528611, upload-time = "2026-03-02T16:04:42.097Z" }, - { url = "https://files.pythonhosted.org/packages/cd/7d/5936c7a03a0b0cb0fa0cc425998821c6029756b0855a8f7ee70fba1de955/sqlalchemy-2.0.48-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7a936f1bb23d370b7c8cc079d5fce4c7d18da87a33c6744e51a93b0f9e97e9b3", size = 3472326, upload-time = "2026-03-02T15:57:54.423Z" }, - { url = "https://files.pythonhosted.org/packages/f4/33/cea7dfc31b52904efe3dcdc169eb4514078887dff1f5ae28a7f4c5d54b3c/sqlalchemy-2.0.48-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e004aa9248e8cb0a5f9b96d003ca7c1c0a5da8decd1066e7b53f59eb8ce7c62b", size = 3478453, upload-time = "2026-03-02T16:04:44.584Z" }, - { url = "https://files.pythonhosted.org/packages/c8/95/32107c4d13be077a9cae61e9ae49966a35dc4bf442a8852dd871db31f62e/sqlalchemy-2.0.48-cp314-cp314t-win32.whl", hash = "sha256:b8438ec5594980d405251451c5b7ea9aa58dda38eb7ac35fb7e4c696712ee24f", size = 2147209, upload-time = "2026-03-02T15:52:54.274Z" }, - { url = "https://files.pythonhosted.org/packages/d2/d7/1e073da7a4bc645eb83c76067284a0374e643bc4be57f14cc6414656f92c/sqlalchemy-2.0.48-cp314-cp314t-win_amd64.whl", hash = "sha256:d854b3970067297f3a7fbd7a4683587134aa9b3877ee15aa29eea478dc68f933", size = 2182198, upload-time = "2026-03-02T15:52:55.606Z" }, - { url = "https://files.pythonhosted.org/packages/46/2c/9664130905f03db57961b8980b05cab624afd114bf2be2576628a9f22da4/sqlalchemy-2.0.48-py3-none-any.whl", hash = "sha256:a66fe406437dd65cacd96a72689a3aaaecaebbcd62d81c5ac1c0fdbeac835096", size = 1940202, upload-time = "2026-03-02T15:52:43.285Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/09/45/461788f35e0364a8da7bda51a1fe1b09762d0c32f12f63727998d85a873b/sqlalchemy-2.0.49.tar.gz", hash = "sha256:d15950a57a210e36dd4cec1aac22787e2a4d57ba9318233e2ef8b2daf9ff2d5f", size = 9898221, upload-time = "2026-04-03T16:38:11.704Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/60/b5/e3617cc67420f8f403efebd7b043128f94775e57e5b84e7255203390ceae/sqlalchemy-2.0.49-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c5070135e1b7409c4161133aa525419b0062088ed77c92b1da95366ec5cbebbe", size = 2159126, upload-time = "2026-04-03T16:50:13.242Z" }, + { url = "https://files.pythonhosted.org/packages/20/9b/91ca80403b17cd389622a642699e5f6564096b698e7cdcbcbb6409898bc4/sqlalchemy-2.0.49-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9ac7a3e245fd0310fd31495eb61af772e637bdf7d88ee81e7f10a3f271bff014", size = 3315509, upload-time = "2026-04-03T16:54:49.332Z" }, + { url = "https://files.pythonhosted.org/packages/b1/61/0722511d98c54de95acb327824cb759e8653789af2b1944ab1cc69d32565/sqlalchemy-2.0.49-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4d4e5a0ceba319942fa6b585cf82539288a61e314ef006c1209f734551ab9536", size = 3315014, upload-time = "2026-04-03T16:56:56.376Z" }, + { url = "https://files.pythonhosted.org/packages/46/55/d514a653ffeb4cebf4b54c47bec32ee28ad89d39fafba16eeed1d81dccd5/sqlalchemy-2.0.49-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3ddcb27fb39171de36e207600116ac9dfd4ae46f86c82a9bf3934043e80ebb88", size = 3267388, upload-time = "2026-04-03T16:54:51.272Z" }, + { url = "https://files.pythonhosted.org/packages/2f/16/0dcc56cb6d3335c1671a2258f5d2cb8267c9a2260e27fde53cbfb1b3540a/sqlalchemy-2.0.49-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:32fe6a41ad97302db2931f05bb91abbcc65b5ce4c675cd44b972428dd2947700", size = 3289602, upload-time = "2026-04-03T16:56:57.63Z" }, + { url = "https://files.pythonhosted.org/packages/51/6c/f8ab6fb04470a133cd80608db40aa292e6bae5f162c3a3d4ab19544a67af/sqlalchemy-2.0.49-cp311-cp311-win32.whl", hash = "sha256:46d51518d53edfbe0563662c96954dc8fcace9832332b914375f45a99b77cc9a", size = 2119044, upload-time = "2026-04-03T17:00:53.455Z" }, + { url = "https://files.pythonhosted.org/packages/c4/59/55a6d627d04b6ebb290693681d7683c7da001eddf90b60cfcc41ee907978/sqlalchemy-2.0.49-cp311-cp311-win_amd64.whl", hash = "sha256:951d4a210744813be63019f3df343bf233b7432aadf0db54c75802247330d3af", size = 2143642, upload-time = "2026-04-03T17:00:54.769Z" }, + { url = "https://files.pythonhosted.org/packages/49/b3/2de412451330756aaaa72d27131db6dde23995efe62c941184e15242a5fa/sqlalchemy-2.0.49-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4bbccb45260e4ff1b7db0be80a9025bb1e6698bdb808b83fff0000f7a90b2c0b", size = 2157681, upload-time = "2026-04-03T16:53:07.132Z" }, + { url = "https://files.pythonhosted.org/packages/50/84/b2a56e2105bd11ebf9f0b93abddd748e1a78d592819099359aa98134a8bf/sqlalchemy-2.0.49-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fb37f15714ec2652d574f021d479e78cd4eb9d04396dca36568fdfffb3487982", size = 3338976, upload-time = "2026-04-03T17:07:40Z" }, + { url = "https://files.pythonhosted.org/packages/2c/fa/65fcae2ed62f84ab72cf89536c7c3217a156e71a2c111b1305ab6f0690e2/sqlalchemy-2.0.49-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3bb9ec6436a820a4c006aad1ac351f12de2f2dbdaad171692ee457a02429b672", size = 3351937, upload-time = "2026-04-03T17:12:23.374Z" }, + { url = "https://files.pythonhosted.org/packages/f8/2f/6fd118563572a7fe475925742eb6b3443b2250e346a0cc27d8d408e73773/sqlalchemy-2.0.49-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8d6efc136f44a7e8bc8088507eaabbb8c2b55b3dbb63fe102c690da0ddebe55e", size = 3281646, upload-time = "2026-04-03T17:07:41.949Z" }, + { url = "https://files.pythonhosted.org/packages/c5/d7/410f4a007c65275b9cf82354adb4bb8ba587b176d0a6ee99caa16fe638f8/sqlalchemy-2.0.49-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e06e617e3d4fd9e51d385dfe45b077a41e9d1b033a7702551e3278ac597dc750", size = 3316695, upload-time = "2026-04-03T17:12:25.642Z" }, + { url = "https://files.pythonhosted.org/packages/d9/95/81f594aa60ded13273a844539041ccf1e66c5a7bed0a8e27810a3b52d522/sqlalchemy-2.0.49-cp312-cp312-win32.whl", hash = "sha256:83101a6930332b87653886c01d1ee7e294b1fe46a07dd9a2d2b4f91bcc88eec0", size = 2117483, upload-time = "2026-04-03T17:05:40.896Z" }, + { url = "https://files.pythonhosted.org/packages/47/9e/fd90114059175cac64e4fafa9bf3ac20584384d66de40793ae2e2f26f3bb/sqlalchemy-2.0.49-cp312-cp312-win_amd64.whl", hash = "sha256:618a308215b6cececb6240b9abde545e3acdabac7ae3e1d4e666896bf5ba44b4", size = 2144494, upload-time = "2026-04-03T17:05:42.282Z" }, + { url = "https://files.pythonhosted.org/packages/ae/81/81755f50eb2478eaf2049728491d4ea4f416c1eb013338682173259efa09/sqlalchemy-2.0.49-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:df2d441bacf97022e81ad047e1597552eb3f83ca8a8f1a1fdd43cd7fe3898120", size = 2154547, upload-time = "2026-04-03T16:53:08.64Z" }, + { url = "https://files.pythonhosted.org/packages/a2/bc/3494270da80811d08bcfa247404292428c4fe16294932bce5593f215cad9/sqlalchemy-2.0.49-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8e20e511dc15265fb433571391ba313e10dd8ea7e509d51686a51313b4ac01a2", size = 3280782, upload-time = "2026-04-03T17:07:43.508Z" }, + { url = "https://files.pythonhosted.org/packages/cd/f5/038741f5e747a5f6ea3e72487211579d8cbea5eb9827a9cbd61d0108c4bd/sqlalchemy-2.0.49-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:47604cb2159f8bbd5a1ab48a714557156320f20871ee64d550d8bf2683d980d3", size = 3297156, upload-time = "2026-04-03T17:12:27.697Z" }, + { url = "https://files.pythonhosted.org/packages/88/50/a6af0ff9dc954b43a65ca9b5367334e45d99684c90a3d3413fc19a02d43c/sqlalchemy-2.0.49-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:22d8798819f86720bc646ab015baff5ea4c971d68121cb36e2ebc2ee43ead2b7", size = 3228832, upload-time = "2026-04-03T17:07:45.38Z" }, + { url = "https://files.pythonhosted.org/packages/bc/d1/5f6bdad8de0bf546fc74370939621396515e0cdb9067402d6ba1b8afbe9a/sqlalchemy-2.0.49-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9b1c058c171b739e7c330760044803099c7fff11511e3ab3573e5327116a9c33", size = 3267000, upload-time = "2026-04-03T17:12:29.657Z" }, + { url = "https://files.pythonhosted.org/packages/f7/30/ad62227b4a9819a5e1c6abff77c0f614fa7c9326e5a3bdbee90f7139382b/sqlalchemy-2.0.49-cp313-cp313-win32.whl", hash = "sha256:a143af2ea6672f2af3f44ed8f9cd020e9cc34c56f0e8db12019d5d9ecf41cb3b", size = 2115641, upload-time = "2026-04-03T17:05:43.989Z" }, + { url = "https://files.pythonhosted.org/packages/17/3a/7215b1b7d6d49dc9a87211be44562077f5f04f9bb5a59552c1c8e2d98173/sqlalchemy-2.0.49-cp313-cp313-win_amd64.whl", hash = "sha256:12b04d1db2663b421fe072d638a138460a51d5a862403295671c4f3987fb9148", size = 2141498, upload-time = "2026-04-03T17:05:45.7Z" }, + { url = "https://files.pythonhosted.org/packages/28/4b/52a0cb2687a9cd1648252bb257be5a1ba2c2ded20ba695c65756a55a15a4/sqlalchemy-2.0.49-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:24bd94bb301ec672d8f0623eba9226cc90d775d25a0c92b5f8e4965d7f3a1518", size = 3560807, upload-time = "2026-04-03T16:58:31.666Z" }, + { url = "https://files.pythonhosted.org/packages/8c/d8/fda95459204877eed0458550d6c7c64c98cc50c2d8d618026737de9ed41a/sqlalchemy-2.0.49-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a51d3db74ba489266ef55c7a4534eb0b8db9a326553df481c11e5d7660c8364d", size = 3527481, upload-time = "2026-04-03T17:06:00.155Z" }, + { url = "https://files.pythonhosted.org/packages/ff/0a/2aac8b78ac6487240cf7afef8f203ca783e8796002dc0cf65c4ee99ff8bb/sqlalchemy-2.0.49-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:55250fe61d6ebfd6934a272ee16ef1244e0f16b7af6cd18ab5b1fc9f08631db0", size = 3468565, upload-time = "2026-04-03T16:58:33.414Z" }, + { url = "https://files.pythonhosted.org/packages/a5/3d/ce71cfa82c50a373fd2148b3c870be05027155ce791dc9a5dcf439790b8b/sqlalchemy-2.0.49-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:46796877b47034b559a593d7e4b549aba151dae73f9e78212a3478161c12ab08", size = 3477769, upload-time = "2026-04-03T17:06:02.787Z" }, + { url = "https://files.pythonhosted.org/packages/d5/e8/0a9f5c1f7c6f9ca480319bf57c2d7423f08d31445974167a27d14483c948/sqlalchemy-2.0.49-cp313-cp313t-win32.whl", hash = "sha256:9c4969a86e41454f2858256c39bdfb966a20961e9b58bf8749b65abf447e9a8d", size = 2143319, upload-time = "2026-04-03T17:02:04.328Z" }, + { url = "https://files.pythonhosted.org/packages/0e/51/fb5240729fbec73006e137c4f7a7918ffd583ab08921e6ff81a999d6517a/sqlalchemy-2.0.49-cp313-cp313t-win_amd64.whl", hash = "sha256:b9870d15ef00e4d0559ae10ee5bc71b654d1f20076dbe8bc7ed19b4c0625ceba", size = 2175104, upload-time = "2026-04-03T17:02:05.989Z" }, + { url = "https://files.pythonhosted.org/packages/55/33/bf28f618c0a9597d14e0b9ee7d1e0622faff738d44fe986ee287cdf1b8d0/sqlalchemy-2.0.49-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:233088b4b99ebcbc5258c755a097aa52fbf90727a03a5a80781c4b9c54347a2e", size = 2156356, upload-time = "2026-04-03T16:53:09.914Z" }, + { url = "https://files.pythonhosted.org/packages/d1/a7/5f476227576cb8644650eff68cc35fa837d3802b997465c96b8340ced1e2/sqlalchemy-2.0.49-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:57ca426a48eb2c682dae8204cd89ea8ab7031e2675120a47924fabc7caacbc2a", size = 3276486, upload-time = "2026-04-03T17:07:46.9Z" }, + { url = "https://files.pythonhosted.org/packages/2e/84/efc7c0bf3a1c5eef81d397f6fddac855becdbb11cb38ff957888603014a7/sqlalchemy-2.0.49-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:685e93e9c8f399b0c96a624799820176312f5ceef958c0f88215af4013d29066", size = 3281479, upload-time = "2026-04-03T17:12:32.226Z" }, + { url = "https://files.pythonhosted.org/packages/91/68/bb406fa4257099c67bd75f3f2261b129c63204b9155de0d450b37f004698/sqlalchemy-2.0.49-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:9e0400fa22f79acc334d9a6b185dc00a44a8e6578aa7e12d0ddcd8434152b187", size = 3226269, upload-time = "2026-04-03T17:07:48.678Z" }, + { url = "https://files.pythonhosted.org/packages/67/84/acb56c00cca9f251f437cb49e718e14f7687505749ea9255d7bd8158a6df/sqlalchemy-2.0.49-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:a05977bffe9bffd2229f477fa75eabe3192b1b05f408961d1bebff8d1cd4d401", size = 3248260, upload-time = "2026-04-03T17:12:34.381Z" }, + { url = "https://files.pythonhosted.org/packages/56/19/6a20ea25606d1efd7bd1862149bb2a22d1451c3f851d23d887969201633f/sqlalchemy-2.0.49-cp314-cp314-win32.whl", hash = "sha256:0f2fa354ba106eafff2c14b0cc51f22801d1e8b2e4149342023bd6f0955de5f5", size = 2118463, upload-time = "2026-04-03T17:05:47.093Z" }, + { url = "https://files.pythonhosted.org/packages/cf/4f/8297e4ed88e80baa1f5aa3c484a0ee29ef3c69c7582f206c916973b75057/sqlalchemy-2.0.49-cp314-cp314-win_amd64.whl", hash = "sha256:77641d299179c37b89cf2343ca9972c88bb6eef0d5fc504a2f86afd15cd5adf5", size = 2144204, upload-time = "2026-04-03T17:05:48.694Z" }, + { url = "https://files.pythonhosted.org/packages/1f/33/95e7216df810c706e0cd3655a778604bbd319ed4f43333127d465a46862d/sqlalchemy-2.0.49-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c1dc3368794d522f43914e03312202523cc89692f5389c32bea0233924f8d977", size = 3565474, upload-time = "2026-04-03T16:58:35.128Z" }, + { url = "https://files.pythonhosted.org/packages/0c/a4/ed7b18d8ccf7f954a83af6bb73866f5bc6f5636f44c7731fbb741f72cc4f/sqlalchemy-2.0.49-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7c821c47ecfe05cc32140dcf8dc6fd5d21971c86dbd56eabfe5ba07a64910c01", size = 3530567, upload-time = "2026-04-03T17:06:04.587Z" }, + { url = "https://files.pythonhosted.org/packages/73/a3/20faa869c7e21a827c4a2a42b41353a54b0f9f5e96df5087629c306df71e/sqlalchemy-2.0.49-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:9c04bff9a5335eb95c6ecf1c117576a0aa560def274876fd156cfe5510fccc61", size = 3474282, upload-time = "2026-04-03T16:58:37.131Z" }, + { url = "https://files.pythonhosted.org/packages/b7/50/276b9a007aa0764304ad467eceb70b04822dc32092492ee5f322d559a4dc/sqlalchemy-2.0.49-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:7f605a456948c35260e7b2a39f8952a26f077fd25653c37740ed186b90aaa68a", size = 3480406, upload-time = "2026-04-03T17:06:07.176Z" }, + { url = "https://files.pythonhosted.org/packages/e5/c3/c80fcdb41905a2df650c2a3e0337198b6848876e63d66fe9188ef9003d24/sqlalchemy-2.0.49-cp314-cp314t-win32.whl", hash = "sha256:6270d717b11c5476b0cbb21eedc8d4dbb7d1a956fd6c15a23e96f197a6193158", size = 2149151, upload-time = "2026-04-03T17:02:07.281Z" }, + { url = "https://files.pythonhosted.org/packages/05/52/9f1a62feab6ed368aff068524ff414f26a6daebc7361861035ae00b05530/sqlalchemy-2.0.49-cp314-cp314t-win_amd64.whl", hash = "sha256:275424295f4256fd301744b8f335cff367825d270f155d522b30c7bf49903ee7", size = 2184178, upload-time = "2026-04-03T17:02:08.623Z" }, + { url = "https://files.pythonhosted.org/packages/e5/30/8519fdde58a7bdf155b714359791ad1dc018b47d60269d5d160d311fdc36/sqlalchemy-2.0.49-py3-none-any.whl", hash = "sha256:ec44cfa7ef1a728e88ad41674de50f6db8cfdb3e2af84af86e0041aaf02d43d0", size = 1942158, upload-time = "2026-04-03T16:53:44.135Z" }, ] [[package]] name = "sqlmodel" -version = "0.0.37" +version = "0.0.38" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pydantic" }, { name = "sqlalchemy" }, + { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fb/26/1d2faa0fd5a765267f49751de533adac6b9ff9366c7c6e7692df4f32230f/sqlmodel-0.0.37.tar.gz", hash = "sha256:d2c19327175794faf50b1ee31cc966764f55b1dedefc046450bc5741a3d68352", size = 85527, upload-time = "2026-02-21T16:39:47.038Z" } +sdist = { url = "https://files.pythonhosted.org/packages/64/0d/26ec1329960ea9430131fe63f63a95ea4cb8971d49c891ff7e1f3255421c/sqlmodel-0.0.38.tar.gz", hash = "sha256:d583ec237b14103809f74e8630032bc40ab68cd6b754a610f0813c56911a547b", size = 86710, upload-time = "2026-04-02T21:03:55.571Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b1/e1/7c8d18e737433f3b5bbe27b56a9072a9fcb36342b48f1bef34b6da1d61f2/sqlmodel-0.0.37-py3-none-any.whl", hash = "sha256:2137a4045ef3fd66a917a7717ada959a1ceb3630d95e1f6aaab39dd2c0aef278", size = 27224, upload-time = "2026-02-21T16:39:47.781Z" }, + { url = "https://files.pythonhosted.org/packages/72/c7/10c60af0607ab6fa136264f7f39d205932218516226d38585324ffda705d/sqlmodel-0.0.38-py3-none-any.whl", hash = "sha256:84e3fa990a77395461ded72a6c73173438ce8449d5c1c4d97fbff1b1df692649", size = 27294, upload-time = "2026-04-02T21:03:56.406Z" }, ] [[package]] @@ -3171,11 +3172,11 @@ wheels = [ [[package]] name = "tzdata" -version = "2025.3" +version = "2026.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5e/a7/c202b344c5ca7daf398f3b8a477eeb205cf3b6f32e7ec3a6bac0629ca975/tzdata-2025.3.tar.gz", hash = "sha256:de39c2ca5dc7b0344f2eba86f49d614019d29f060fc4ebc8a417896a620b56a7", size = 196772, upload-time = "2025-12-13T17:45:35.667Z" } +sdist = { url = "https://files.pythonhosted.org/packages/19/f5/cd531b2d15a671a40c0f66cf06bc3570a12cd56eef98960068ebbad1bf5a/tzdata-2026.1.tar.gz", hash = "sha256:67658a1903c75917309e753fdc349ac0efd8c27db7a0cb406a25be4840f87f98", size = 197639, upload-time = "2026-04-03T11:25:22.002Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c7/b0/003792df09decd6849a5e39c28b513c06e84436a54440380862b5aeff25d/tzdata-2025.3-py2.py3-none-any.whl", hash = "sha256:06a47e5700f3081aab02b2e513160914ff0694bce9947d6b76ebd6bf57cfc5d1", size = 348521, upload-time = "2025-12-13T17:45:33.889Z" }, + { url = "https://files.pythonhosted.org/packages/b0/70/d460bd685a170790ec89317e9bd33047988e4bce507b831f5db771e142de/tzdata-2026.1-py2.py3-none-any.whl", hash = "sha256:4b1d2be7ac37ceafd7327b961aa3a54e467efbdb563a23655fbfe0d39cfc42a9", size = 348952, upload-time = "2026-04-03T11:25:20.313Z" }, ] [[package]] @@ -3189,15 +3190,15 @@ wheels = [ [[package]] name = "uvicorn" -version = "0.42.0" +version = "0.44.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, { name = "h11" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e3/ad/4a96c425be6fb67e0621e62d86c402b4a17ab2be7f7c055d9bd2f638b9e2/uvicorn-0.42.0.tar.gz", hash = "sha256:9b1f190ce15a2dd22e7758651d9b6d12df09a13d51ba5bf4fc33c383a48e1775", size = 85393, upload-time = "2026-03-16T06:19:50.077Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5e/da/6eee1ff8b6cbeed47eeb5229749168e81eb4b7b999a1a15a7176e51410c9/uvicorn-0.44.0.tar.gz", hash = "sha256:6c942071b68f07e178264b9152f1f16dfac5da85880c4ce06366a96d70d4f31e", size = 86947, upload-time = "2026-04-06T09:23:22.826Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/0a/89/f8827ccff89c1586027a105e5630ff6139a64da2515e24dafe860bd9ae4d/uvicorn-0.42.0-py3-none-any.whl", hash = "sha256:96c30f5c7abe6f74ae8900a70e92b85ad6613b745d4879eb9b16ccad15645359", size = 68830, upload-time = "2026-03-16T06:19:48.325Z" }, + { url = "https://files.pythonhosted.org/packages/b7/23/a5bbd9600dd607411fa644c06ff4951bec3a4d82c4b852374024359c19c0/uvicorn-0.44.0-py3-none-any.whl", hash = "sha256:ce937c99a2cc70279556967274414c087888e8cec9f9c94644dfca11bd3ced89", size = 69425, upload-time = "2026-04-06T09:23:21.524Z" }, ] [[package]]