diff --git a/assets/tailwind-theme.css b/assets/tailwind-theme.css index f034de789..ffd79ebb7 100644 --- a/assets/tailwind-theme.css +++ b/assets/tailwind-theme.css @@ -163,9 +163,66 @@ --font-instrument-sans: 'Instrument Sans', sans-serif; --font-source-code-pro: 'Source Code Pro', monospace; --font-jetbrains: "JetBrains Mono", monospace; + /* Shadow (light) */ + --shadow-small-adaptive: 0px 2px 5px 0px rgba(28, 32, 36, 0.03); + --shadow-medium-adaptive: 0px 4px 8px 0px rgba(28, 32, 36, 0.04); + --shadow-large-adaptive: + 0px 24px 12px 0px rgba(28, 32, 36, 0.02), + 0px 8px 8px 0px rgba(28, 32, 36, 0.02), + 0px 2px 6px 0px rgba(28, 32, 36, 0.02); + --shadow-large-negative-adaptive: + 0px -24px 12px 0px rgba(28, 32, 36, 0.02), + 0px -8px 8px 0px rgba(28, 32, 36, 0.02), + 0px -2px 6px 0px rgba(28, 32, 36, 0.02); + --shadow-large-negative-primary-adaptive: + 0px -32px 48px 0px color-mix(in srgb, var(--primary-9) 8%, transparent), + 0px -16px 32px 0px color-mix(in srgb, var(--primary-9) 10%, transparent), + 0px -8px 24px 0px color-mix(in srgb, var(--primary-9) 12%, transparent), + 0px -2px 16px 0px color-mix(in srgb, var(--primary-9) 14%, transparent); + --shadow-x-large-adaptive: + 0 0 0 1px rgba(0, 0, 0, 0.04), + 0 4px 8px 0 rgba(0, 0, 0, 0.02), + 0 1px 1px 0 rgba(0, 0, 0, 0.01), + 0 4px 8px 0 rgba(0, 0, 0, 0.03), + 0 0 0 1px #fff inset; + --shadow-inner-adaptive: 0 6px 16px 0 rgba(0, 0, 0, 0.04) inset; + --shadow-button-outline-adaptive: + 0 -1px 0 0 rgba(0, 0, 0, 0.08) inset, + 0 0 0 1px rgba(0, 0, 0, 0.08) inset, + 0 1px 2px 0 rgba(0, 0, 0, 0.02), + 0 1px 4px 0 rgba(0, 0, 0, 0.02); + --shadow-card-xs-no-left-adaptive: + 0 -1px 0 0 rgba(0, 0, 0, 0.16) inset, + 0 1px 0 0 rgba(0, 0, 0, 0.08) inset, + -1px 0 0 0 rgba(0, 0, 0, 0.08) inset, + 0 1px 2px 0 rgba(0, 0, 0, 0.02), + 0 1px 4px 0 rgba(0, 0, 0, 0.02); + --shadow-card-small-adaptive: + 0 0 0 1px rgba(0, 0, 0, 0.04), + 0 4px 8px 0 rgba(0, 0, 0, 0.04), + 0 1px 1px 0 rgba(0, 0, 0, 0.01), + 0 2px 4px 0 rgba(0, 0, 0, 0.03); color-scheme: light dark; } +.dark { + /* Shadow (dark) */ + --shadow-small-adaptive: none; + --shadow-medium-adaptive: none; + --shadow-large-adaptive: none; + --shadow-large-negative-adaptive: none; + --shadow-large-negative-primary-adaptive: + 0px -32px 48px 0px color-mix(in srgb, var(--primary-9) 4%, transparent), + 0px -16px 32px 0px color-mix(in srgb, var(--primary-9) 8%, transparent), + 0px -8px 24px 0px color-mix(in srgb, var(--primary-9) 12%, transparent), + 0px -2px 16px 0px color-mix(in srgb, var(--primary-9) 16%, transparent); + --shadow-x-large-adaptive: none; + --shadow-inner-adaptive: none; + --shadow-button-outline-adaptive: none; + --shadow-card-xs-no-left-adaptive: none; + --shadow-card-small-adaptive: none; +} + @theme { /* Custom Palette */ --color-white-1: var(--c-white-1); @@ -1104,20 +1161,23 @@ --font-mono: var(--font-jetbrains); /* Shadow */ --shadow-none: none; - --shadow-small: 0px 2px 5px 0px light-dark(rgba(28, 32, 36, 0.03), rgba(0, 0, 0, 0)); - --shadow-medium: 0px 4px 8px 0px light-dark(rgba(28, 32, 36, 0.04), rgba(0, 0, 0, 0)); - --shadow-large: - 0px 24px 12px 0px light-dark(rgba(28, 32, 36, 0.02), rgba(0, 0, 0, 0)), - 0px 8px 8px 0px light-dark(rgba(28, 32, 36, 0.02), rgba(0, 0, 0, 0)), - 0px 2px 6px 0px light-dark(rgba(28, 32, 36, 0.02), rgba(0, 0, 0, 0)); - --shadow-large-negative: - 0px -24px 12px 0px light-dark(rgba(28, 32, 36, 0.02), rgba(0, 0, 0, 0)), - 0px -8px 8px 0px light-dark(rgba(28, 32, 36, 0.02), rgba(0, 0, 0, 0)), - 0px -2px 6px 0px light-dark(rgba(28, 32, 36, 0.02), rgba(0, 0, 0, 0)); + --shadow-small: var(--shadow-small-adaptive); + --shadow-medium: var(--shadow-medium-adaptive); + --shadow-large: var(--shadow-large-adaptive); + --shadow-large-negative: var(--shadow-large-negative-adaptive); + --shadow-large-negative-primary: var(--shadow-large-negative-primary-adaptive); + --shadow-x-large: var(--shadow-x-large-adaptive); + --shadow-inner: var(--shadow-inner-adaptive); --shadow-card: 0 0 0 1px rgba(0, 0, 0, 0.04), 0 4px 8px 0 rgba(0, 0, 0, 0.07), 0 1px 1px 0 rgba(0, 0, 0, 0.01), 0 0 0 1px #FFF inset; + --shadow-button-bordered: + 0 0 0 1px var(--primary-9) inset, + 0 2px 0 0 rgba(255, 255, 255, 0.22) inset; + --shadow-button-outline: var(--shadow-button-outline-adaptive); + --shadow-card-xs-no-left: var(--shadow-card-xs-no-left-adaptive); + --shadow-card-small: var(--shadow-card-small-adaptive); --shadow-card-dark: 0 0 0 1px var(--m-slate-9, #2A3037); --text-xs: 0.8125rem; --text-xs--line-height: 1.25rem; diff --git a/pcweb/constants.py b/pcweb/constants.py index ffbbbf6ba..f2544c95f 100644 --- a/pcweb/constants.py +++ b/pcweb/constants.py @@ -14,6 +14,10 @@ "REFLEX_DEV_WEB_GENERAL_FORM_FEEDBACK_WEBHOOK_URL" ) +REPLICATE_FLUX_SCHNELL_PREDICTIONS_URL: str = ( + "https://api.replicate.com/v1/models/black-forest-labs/flux-schnell/predictions" +) + # pcweb urls. REFLEX_URL = "https://reflex.dev/" REFLEX_DOCS_URL = "https://reflex.dev/docs/getting-started/introduction/" diff --git a/pcweb/pages/__init__.py b/pcweb/pages/__init__.py index 9d8f6b264..e6e3735fa 100644 --- a/pcweb/pages/__init__.py +++ b/pcweb/pages/__init__.py @@ -13,7 +13,6 @@ from .errors import errors as errors from .faq import faq as faq from .framework.framework import framework as framework -from .gallery import gallery as gallery from .gallery.apps import gallery_apps_routes from .hosting.hosting import hosting_landing as hosting_landing from .landing.landing import landing as landing @@ -32,6 +31,8 @@ from .pricing.pricing import pricing as pricing from .sales import sales as sales from .security.security import security_page as security_page +from .templates import templates_landing as templates_landing +from .templates.template_details import template_details as template_details from .to_be_booked import to_be_booked as to_be_booked from .use_cases.consulting import consulting_use_case_page as consulting_use_case_page from .use_cases.finance import finance_use_case_page as finance_use_case_page diff --git a/pcweb/pages/framework/demos/image_gen/image_gen.py b/pcweb/pages/framework/demos/image_gen/image_gen.py index 56cc1a5d5..66ae5ff7b 100644 --- a/pcweb/pages/framework/demos/image_gen/image_gen.py +++ b/pcweb/pages/framework/demos/image_gen/image_gen.py @@ -1,8 +1,46 @@ +import os + +import aiohttp import reflex as rx -import replicate from pcweb.components.button import button from pcweb.components.icons import get_icon +from pcweb.constants import REPLICATE_FLUX_SCHNELL_PREDICTIONS_URL +from pcweb.utils.http import default_client + +_REPLICATE_WAIT_SEC = 5 + +_FLUX_SCHNELL_INPUT_DEFAULTS: dict[str, str | int | bool] = { + "go_fast": True, + "num_inference_steps": 3, + "num_outputs": 1, + "aspect_ratio": "1:1", + "output_format": "webp", + "output_quality": 80, +} + + +async def _run_flux_schnell(prompt: str) -> str: + token = os.environ.get("REPLICATE_API_TOKEN") + + session = default_client() + timeout = aiohttp.ClientTimeout(total=_REPLICATE_WAIT_SEC) + async with session.post( + REPLICATE_FLUX_SCHNELL_PREDICTIONS_URL, + json={"input": {"prompt": prompt, **_FLUX_SCHNELL_INPUT_DEFAULTS}}, + headers={ + "Authorization": f"Token {token}", + "Prefer": f"wait={_REPLICATE_WAIT_SEC}", + }, + timeout=timeout, + ) as resp: + resp.raise_for_status() + prediction = await resp.json() + + output = prediction.get("output") + if not output: + raise RuntimeError("Replicate returned no output") + return str(output[0] if isinstance(output, list) else output) class ImageGenState(rx.State): @@ -20,15 +58,11 @@ async def get_image(self, form_data): async with self: self.processing = True yield - input = {"prompt": prompt} try: - output = await replicate.async_run( - "black-forest-labs/flux-schnell", - input=input, - ) + image_url = await _run_flux_schnell(prompt) async with self: - self.image_url = str(output[0]) + self.image_url = image_url except Exception: async with self: self.image_url = "" @@ -106,7 +140,15 @@ def get_image(self, form_data): self.processing = True yield - input = {"prompt": prompt} + input = { + "prompt": prompt, + "go_fast": True, + "num_inference_steps": 3, + "num_outputs": 1, + "aspect_ratio": "1:1", + "output_format": "webp", + "output_quality": 80, + } output = replicate.run( "black-forest-labs/flux-schnell", input=input, diff --git a/pcweb/pages/framework/views/footer_index.py b/pcweb/pages/framework/views/footer_index.py index a6b1e00a9..65b3b08f8 100644 --- a/pcweb/pages/framework/views/footer_index.py +++ b/pcweb/pages/framework/views/footer_index.py @@ -59,7 +59,7 @@ def logo() -> rx.Component: class_name="shrink-0 hidden dark:block", ), to="/", - class_name="block shrink-0 mr-[7rem]", + class_name="block shrink-0 mr-[7rem] md:hidden xl:block", ) @@ -199,7 +199,7 @@ def newsletter() -> rx.Component: @rx.memo -def footer_index() -> rx.Component: +def footer_index(class_name: str = "", grid_class_name: str = "") -> rx.Component: return rx.el.footer( rx.el.div( logo(), @@ -235,7 +235,10 @@ def footer_index() -> rx.Component: rx.el.div( class_name="absolute -top-24 -right-px w-px h-24 bg-gradient-to-b from-transparent to-current text-m-slate-4 dark:text-m-slate-10 max-lg:hidden" ), - class_name="grid grid-cols-1 lg:grid-cols-3 gap-12 w-full lg:pr-12 pb-8 lg:border-r border-m-slate-4 dark:border-m-slate-10 ml-auto relative", + class_name=ui.cn( + "grid grid-cols-1 lg:grid-cols-3 gap-12 w-full lg:pr-12 pb-8 lg:border-r border-m-slate-4 dark:border-m-slate-10 xl:ml-auto relative", + grid_class_name, + ), ), rx.el.div( newsletter(), @@ -259,5 +262,8 @@ def footer_index() -> rx.Component: ), class_name="flex flex-row items-center justify-between py-6 gap-4 w-full border-t border-m-slate-4 dark:border-m-slate-10 relative", ), - class_name="flex flex-col max-w-(--docs-layout-max-width) justify-center items-center w-full mx-auto mt-24 max-lg:px-4 overflow-hidden", + class_name=ui.cn( + "flex flex-col max-w-(--docs-layout-max-width) justify-center items-center w-full mx-auto mt-24 max-lg:px-4 overflow-hidden", + class_name, + ), ) diff --git a/pcweb/pages/templates/__init__.py b/pcweb/pages/templates/__init__.py new file mode 100644 index 000000000..1a47bdd36 --- /dev/null +++ b/pcweb/pages/templates/__init__.py @@ -0,0 +1,36 @@ +import reflex as rx + +from pcweb.meta.meta import create_meta_tags +from pcweb.pages.framework.views.footer_index import footer_index +from pcweb.pages.templates.templates_state import TemplatesState +from pcweb.pages.templates.views import templates_grid, templates_sidebar +from pcweb.templates.secondary_page import secondary_page + + +@secondary_page( + path="/templates", + title="Reflex Templates - Python Dashboards & Tools", + description="Reflex templates: dashboards, chatbots, data tools, and AI apps. Start from a template and customize in Python.", + meta=create_meta_tags( + title="Reflex Templates - Python Dashboards & Tools", + description="Reflex templates: dashboards, chatbots, data tools, and AI apps. Start from a template and customize in Python.", + image="/previews/index_preview.webp", + ), + on_load=TemplatesState.load_templates, +) +def templates_landing() -> rx.Component: + return rx.el.div( + rx.el.div( + templates_sidebar(), + rx.el.div( + templates_grid(), + footer_index( + class_name="max-w-[109rem] px-4 xl:px-14", + grid_class_name="w-fit [&>div]:min-w-[9rem]", + ), + class_name="flex flex-col flex-1 min-w-0", + ), + class_name="flex flex-row w-full max-w-[108rem] mx-auto min-h-screen", + ), + class_name="flex flex-col w-full relative overflow-x-clip", + ) diff --git a/pcweb/pages/templates/template_details/__init__.py b/pcweb/pages/templates/template_details/__init__.py new file mode 100644 index 000000000..551d87997 --- /dev/null +++ b/pcweb/pages/templates/template_details/__init__.py @@ -0,0 +1,54 @@ +import reflex as rx + +from pcweb.pages.framework.views.footer_index import footer_index +from pcweb.pages.templates.template_details.views import ( + about, + faq, + header, + others, + sidebar, + sidebar_mobile, + social, +) +from pcweb.pages.templates.templates_state import TemplatesState +from pcweb.templates.secondary_page import secondary_page + + +@secondary_page( + path="/templates/[template_name]/[template_id]", + title="Template Details", + description="Template Details", + image="/previews/index_preview.webp", + on_load=TemplatesState.load_template_details, +) +def template_details(): + return rx.el.div( + rx.cond( + TemplatesState.faq_jsonld, + rx.el.script(TemplatesState.faq_jsonld, type="application/ld+json"), + ), + rx.cond( + TemplatesState.active_template, + rx.el.div( + rx.el.div( + header(), + sidebar_mobile(), + rx.cond(TemplatesState.active_template.about, about()), + rx.cond(TemplatesState.active_template.quotes, social()), + rx.cond(TemplatesState.active_template.faq, faq()), + others(), + class_name="flex-1 flex flex-col divide-y divide-secondary-4 xl:border-r border-secondary-4 pl-4 pr-4 md:pr-0 md:pl-6 3xl:px-0", + ), + sidebar(), + class_name="flex flex-row w-full max-w-[108rem] mx-auto min-h-screen", + ), + ), + rx.el.hr( + class_name="h-[1px] w-full bg-m-slate-4 dark:bg-m-slate-10", + ), + footer_index( + class_name="max-w-[109rem]", + grid_class_name="w-fit [&>div]:min-w-[9rem]", + ), + class_name="flex flex-col w-full relative", + ) diff --git a/pcweb/pages/templates/template_details/views/__init__.py b/pcweb/pages/templates/template_details/views/__init__.py new file mode 100644 index 000000000..f05d8224c --- /dev/null +++ b/pcweb/pages/templates/template_details/views/__init__.py @@ -0,0 +1,16 @@ +from .about import about +from .faq import faq +from .header import header +from .others import others +from .sidebar import sidebar, sidebar_mobile +from .social import social + +__all__ = [ + "about", + "faq", + "header", + "others", + "sidebar", + "sidebar_mobile", + "social", +] diff --git a/pcweb/pages/templates/template_details/views/about.py b/pcweb/pages/templates/template_details/views/about.py new file mode 100644 index 000000000..2a0d93d29 --- /dev/null +++ b/pcweb/pages/templates/template_details/views/about.py @@ -0,0 +1,17 @@ +import reflex as rx + +from pcweb.pages.templates.templates_state import TemplatesState + + +def about(): + return rx.el.div( + rx.el.h2( + "About This Template", + class_name="text-2xl font-[575] text-secondary-12", + ), + rx.el.p( + TemplatesState.active_template.about, + class_name="text-sm text-secondary-11 font-[475]", + ), + class_name="xl:pr-16 xl:py-16 py-6 flex flex-col gap-6 w-full", + ) diff --git a/pcweb/pages/templates/template_details/views/faq.py b/pcweb/pages/templates/template_details/views/faq.py new file mode 100644 index 000000000..7e2abd7f4 --- /dev/null +++ b/pcweb/pages/templates/template_details/views/faq.py @@ -0,0 +1,44 @@ +import reflex as rx +import reflex_ui as ui + +from pcweb.pages.templates.templates_state import TemplatesState + + +def faq_item(faq: rx.Var[dict[str, str]]) -> rx.Component: + return ui.accordion.item( + ui.accordion.header( + ui.accordion.trigger( + faq.get("question", ""), + ui.icon( + "PlusSignIcon", + class_name="lg:size-4.5 size-4 shrink-0 transition-transform ease-out group-data-[panel-open]:rotate-45", + data_slot="accordion-trigger-icon", + ), + unstyled=True, + class_name="text-secondary-12 lg:text-lg text-base font-semibold lg:py-6 py-4 flex-row group relative flex w-full items-center justify-between gap-4 text-md transition-colors disabled:cursor-not-allowed disabled:bg-secondary-3 disabled:text-secondary-8 disabled:[&_svg]:text-secondary-8 [&_svg]:text-secondary-11 data-[panel-open]:text-primary-11 ease-out", + ), + unstyled=True, + ), + ui.accordion.panel( + rx.el.div(faq.get("answer", ""), class_name="lg:pb-6 pb-4"), + unstyled=True, + class_name="h-[var(--accordion-panel-height)] overflow-hidden text-sm text-secondary-11 font-medium transition-[height,opacity] ease-out data-[ending-style]:h-0 data-[starting-style]:h-0", + ), + unstyled=True, + ) + + +def faq(): + return rx.el.div( + rx.el.h2( + "Frequently Asked Questions", + class_name="text-2xl font-[575] text-secondary-12", + ), + ui.accordion.root( + rx.foreach(TemplatesState.active_template.faq, faq_item), + unstyled=True, + class_name="w-full flex flex-col justify-center divide-y divide-dashed divide-secondary-4 overflow-hidden", + multiple=True, + ), + class_name="xl:pr-16 xl:py-16 py-6 flex flex-col gap-6 w-full", + ) diff --git a/pcweb/pages/templates/template_details/views/header.py b/pcweb/pages/templates/template_details/views/header.py new file mode 100644 index 000000000..053453a70 --- /dev/null +++ b/pcweb/pages/templates/template_details/views/header.py @@ -0,0 +1,118 @@ +import reflex as rx +import reflex_ui as ui + +from pcweb.components.marketing_button import button as marketing_button +from pcweb.constants import SCREENSHOT_BUCKET +from pcweb.pages.templates.templates_state import TemplatesState + + +def app_top_bar() -> rx.Component: + return rx.el.div( + rx.el.div( + ui.icon("BrowserIcon"), + rx.el.span( + "Preview", + class_name="text-sm text-secondary-12 font-semibold", + ), + class_name="flex flex-row gap-2.5 items-center h-full border-b border-primary-9", + ), + rx.el.div( + ui.icon("SourceCodeIcon"), + rx.el.span( + "Code", + class_name="text-sm font-medium", + ), + class_name="flex flex-row gap-2.5 items-center h-full text-secondary-11", + ), + rx.el.div( + rx.el.div( + ui.icon("Refresh04Icon"), + class_name="size-9 flex items-center justify-center bg-secondary-1 text-secondary-10", + ), + rx.el.div( + ui.icon("ArrowDown01Icon", class_name="text-secondary-10"), + "/index", + class_name="flex items-center gap-2.5 h-full text-secondary-12 px-2.5 bg-white-1 w-[12.5rem] text-sm font-medium", + ), + rx.el.div( + ui.icon("LinkSquare02Icon"), + class_name="size-9 flex items-center justify-center bg-secondary-1 text-secondary-10", + ), + class_name="flex flex-row items-center rounded-[0.625rem] border border-secondary-5 h-9 overflow-hidden divide-x divide-secondary-5", + ), + rx.el.div( + ui.icon("CpuIcon"), + rx.el.span( + "Workspace is ready", + class_name="text-sm font-medium", + ), + class_name="flex flex-row gap-2.5 items-center rounded-[0.625rem] px-2.5 border border-jade-7 bg-jade-2 h-9 text-jade-11", + ), + class_name="flex flex-row gap-8 items-center bg-secondary-1 pr-16 min-h-[3.25rem] w-full border-y border-secondary-4 max-xl:hidden", + ) + + +def app_image() -> rx.Component: + return rx.el.div( + ui.avatar.root( + ui.avatar.image( + src=f"{SCREENSHOT_BUCKET.strip('/')}/{TemplatesState.active_template.id}", + loading="lazy", + ), + unstyled=True, + class_name="size-full object-cover object-top", + ), + class_name="w-full h-full max-h-[47.75rem] overflow-hidden p-4 bg-white-1", + ) + + +def header() -> rx.Component: + return rx.el.div( + rx.el.div( + rx.el.a( + marketing_button( + ui.icon("ArrowLeft01Icon"), + "Back to Templates", + variant="ghost", + native_button=False, + class_name="px-0", + ), + to="/templates", + class_name="-mb-2", + ), + rx.el.h1( + TemplatesState.active_template.name, + class_name="text-3xl text-secondary-12 font-[575]", + ), + rx.el.div( + rx.cond( + TemplatesState.active_template.last_modified, + rx.el.div( + ui.icon("Clock01Icon", class_name="size-3.5"), + rx.el.span("Updated "), + rx.moment( + TemplatesState.active_template.last_modified, + from_now=True, + ), + class_name="flex flex-row gap-2 items-center text-secondary-12 text-sm font-[525]", + ), + ), + rx.foreach( + TemplatesState.active_template.tags, + lambda tag: rx.el.span( + f"#{tag}", + class_name="text-secondary-10 text-sm font-[525] capitalize", + ), + ), + class_name="flex flex-row gap-x-8 gap-y-2 items-center flex-wrap", + ), + rx.el.p( + TemplatesState.active_template.description, + class_name="text-sm text-secondary-11 font-[475] max-w-[48rem]", + ), + class_name="flex flex-col gap-6 xl:pr-16 xl:pb-16", + ), + app_top_bar(), + app_image(), + class_name="flex flex-col pt-12 w-full", + ) diff --git a/pcweb/pages/templates/template_details/views/others.py b/pcweb/pages/templates/template_details/views/others.py new file mode 100644 index 000000000..198472b63 --- /dev/null +++ b/pcweb/pages/templates/template_details/views/others.py @@ -0,0 +1,35 @@ +import reflex as rx + +from pcweb.components.marketing_button import button as marketing_button +from pcweb.pages.templates.templates_state import TemplatesState +from pcweb.pages.templates.views.templates_grid import template_card + + +def others(): + return rx.el.div( + rx.el.div( + rx.el.h2( + "Related Templates", + class_name="text-2xl font-[575] text-secondary-12", + ), + rx.el.a( + marketing_button( + "Browse All", + variant="outline", + native_button=False, + size="sm", + class_name="w-fit", + ), + to="/templates", + class_name="w-fit", + ), + class_name="flex flex-row gap-4 justify-between items-center", + ), + rx.el.div( + rx.foreach( + TemplatesState.related_templates, lambda t: template_card(template=t) + ), + class_name="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-x-4 gap-y-5", + ), + class_name="xl:pr-16 xl:py-16 py-6 flex flex-col gap-6 w-full", + ) diff --git a/pcweb/pages/templates/template_details/views/sidebar.py b/pcweb/pages/templates/template_details/views/sidebar.py new file mode 100644 index 000000000..25c2816cf --- /dev/null +++ b/pcweb/pages/templates/template_details/views/sidebar.py @@ -0,0 +1,223 @@ +import reflex as rx +import reflex_ui as ui + +from pcweb.components.hosting_banner import HostingBannerState +from pcweb.components.marketing_button import button as marketing_button +from pcweb.constants import INTEGRATIONS_IMAGES_URL +from pcweb.pages.templates.templates_state import TemplatesState + +SECTION_HEADER_CLASS = ( + "font-mono font-[415] text-[0.75rem] leading-4 uppercase pb-4", + "border-b border-dashed dark:border-m-slate-8 border-m-slate-6", + "dark:text-m-slate-6 text-m-slate-7", +) + + +def links_section() -> rx.Component: + return rx.el.div( + marketing_button( + "Create App", + variant="primary", + native_button=False, + class_name="w-full", + on_click=TemplatesState.redirect_to_template( + template_id=TemplatesState.active_template.id, + use_prompt=True, + ), + ), + marketing_button( + "View App", + variant="outline", + native_button=False, + class_name="w-full", + on_click=TemplatesState.redirect_to_template( + template_id=TemplatesState.active_template.id, + use_prompt=False, + ), + ), + class_name="flex flex-col gap-4", + ) + + +def quick_start() -> rx.Component: + return rx.el.div( + rx.el.span("Quick Start", class_name=SECTION_HEADER_CLASS), + rx.el.div( + rx.el.div( + rx.el.span("# Install reflex", class_name="text-[#ACB2BE]"), + rx.el.span( + "pip install reflex", + class_name="text-primary-6 dark:text-primary-11", + ), + rx.el.span("# Use the AI builder", class_name="text-[#ACB2BE]"), + rx.el.span( + "reflex --init ai", + class_name="text-primary-6 dark:text-primary-11", + ), + rx.el.span("# Run the app", class_name="text-[#ACB2BE]"), + rx.el.span( + "reflex run", + class_name="text-primary-6 dark:text-primary-11", + ), + class_name="flex flex-col font-mono text-[0.75rem] leading-5 font-[415] p-4 shadow-[0_1px_0_0_rgba(255,255,255,0.24)_inset] rounded-[0.625rem]", + ), + class_name="rounded-xl bg-[#21252B] p-0.5", + ), + class_name="flex flex-col gap-6", + ) + + +def tech_stack() -> rx.Component: + return rx.el.div( + rx.el.span("Tech Stack", class_name=SECTION_HEADER_CLASS), + rx.el.div( + rx.foreach( + TemplatesState.active_template.tech_stack, + lambda tech: rx.el.span( + tech, + class_name="text-sm font-medium text-secondary-11", + ), + ), + class_name="flex flex-wrap gap-x-4 gap-y-2", + ), + class_name="flex flex-col gap-6", + ) + + +def requirements() -> rx.Component: + return rx.el.div( + rx.el.span("Requirements", class_name=SECTION_HEADER_CLASS), + rx.el.div( + rx.foreach( + TemplatesState.active_template.requirements, + lambda requirement: rx.el.div( + ui.icon("ArrowRight02Icon", class_name="text-primary-10 mt-1"), + rx.el.span( + requirement, + class_name="text-sm font-medium text-secondary-12", + ), + class_name="flex items-start gap-2.5", + ), + ), + class_name="flex flex-col gap-2", + ), + class_name="flex flex-col gap-6", + ) + + +def key_features() -> rx.Component: + return rx.el.div( + rx.el.span("Key Features", class_name=SECTION_HEADER_CLASS), + rx.el.div( + rx.foreach( + TemplatesState.active_template.key_features, + lambda feature: rx.el.div( + ui.icon("Tick02Icon", class_name="text-primary-10 mt-1"), + rx.el.span( + feature, + class_name="text-sm font-medium text-secondary-12", + ), + class_name="flex items-start gap-2.5", + ), + ), + class_name="flex flex-col gap-2", + ), + class_name="flex flex-col gap-6", + ) + + +def enabled_integrations() -> rx.Component: + return rx.el.div( + rx.el.span("Integrations", class_name=SECTION_HEADER_CLASS), + rx.el.div( + integrations_stack( + integrations=TemplatesState.active_template.enabled_integrations + ), + class_name="flex flex-col gap-6", + ), + class_name="flex flex-col gap-6", + ) + + +def enabled_integration_icon(integration_name: str) -> rx.Component: + return ui.avatar.root( + ui.avatar.image( + src=rx.color_mode_cond( + f"{INTEGRATIONS_IMAGES_URL}light/{integration_name}.svg", + f"{INTEGRATIONS_IMAGES_URL}dark/{integration_name}.svg", + ), + unstyled=True, + class_name="size-4", + ), + ui.avatar.fallback( + integration_name[0], + class_name="text-secondary-12 text-base font-semibold uppercase size-4 flex justify-center items-center", + unstyled=True, + ), + unstyled=True, + class_name="size-4 flex items-center justify-center shrink-0", + ) + + +def integrations_stack( + integrations: list[str], +) -> rx.Component: + return rx.el.div( + rx.foreach( + integrations, + lambda integration: integrations_circle(integration_name=integration), + ), + class_name="flex flex-row -space-x-2 flex-wrap gap-y-2", + ) + + +@rx.memo +def integrations_circle(integration_name: str) -> rx.Component: + formatted_integration_name = integration_name.lower().replace("_", " ") + return rx.el.div( + ui.tooltip( + trigger=rx.el.div( + enabled_integration_icon(integration_name=formatted_integration_name), + class_name="flex size-8 shrink-0 justify-center items-center rounded-full shadow-small border border-secondary-a5 bg-white-1 dark:bg-secondary-1 cursor-default", + ), + side="bottom", + content=integration_name, + ), + class_name="z-[1]", + ) + + +def sidebar_content() -> rx.Component: + return rx.fragment( + links_section(), + rx.cond(TemplatesState.active_template.tech_stack, tech_stack()), + rx.cond( + TemplatesState.active_template.enabled_integrations, enabled_integrations() + ), + rx.cond(TemplatesState.active_template.key_features, key_features()), + rx.cond(TemplatesState.active_template.requirements, requirements()), + ) + + +def sidebar_mobile() -> rx.Component: + return rx.el.div( + sidebar_content(), + class_name="flex flex-col gap-12 lg:px-6 px-0 py-6 lg:py-12 lg:hidden", + ) + + +def sidebar() -> rx.Component: + return rx.el.div( + rx.el.div( + sidebar_content(), + class_name="flex flex-col gap-12 p-16 pb-24", + ), + class_name=ui.cn( + "w-full max-w-[23.5rem] shrink-0 overflow-y-auto sticky hidden lg:block [scrollbar-width:thin]", + rx.cond( + HostingBannerState.is_banner_visible, + "top-[103px] h-[calc(100vh-103px)]", + "top-[67px] h-[calc(100vh-67px)]", + ), + ), + ) diff --git a/pcweb/pages/templates/template_details/views/social.py b/pcweb/pages/templates/template_details/views/social.py new file mode 100644 index 000000000..378bdecba --- /dev/null +++ b/pcweb/pages/templates/template_details/views/social.py @@ -0,0 +1,35 @@ +import reflex as rx + +from pcweb.pages.templates.templates_state import TemplatesState + + +def social_card(social: rx.Var[dict[str, str]]) -> rx.Component: + return rx.el.div( + rx.el.h3( + social.get("text", ""), + class_name="text-sm font-[525] text-secondary-12 mb-8", + ), + rx.el.p( + social.get("name", "User"), + class_name="text-sm text-secondary-12 font-[525] mb-2", + ), + rx.el.p( + social.get("role", ""), + class_name="text-sm text-secondary-11 font-[475]", + ), + class_name="flex flex-col bg-white-1 p-6 rounded-[0.625rem] border border-secondary-a4 shadow-small max-w-[25rem] w-full", + ) + + +def social(): + return rx.el.div( + rx.el.h2( + "What Users Say", + class_name="text-2xl font-[575] text-secondary-12", + ), + rx.el.div( + rx.foreach(TemplatesState.active_template.quotes, social_card), + class_name="flex lg:flex-row flex-col gap-4", + ), + class_name="xl:pr-16 xl:py-16 py-6 flex flex-col gap-6 w-full", + ) diff --git a/pcweb/pages/templates/templates_client.py b/pcweb/pages/templates/templates_client.py new file mode 100644 index 000000000..40140b59a --- /dev/null +++ b/pcweb/pages/templates/templates_client.py @@ -0,0 +1,22 @@ +from pcweb.constants import RX_BUILD_BACKEND +from pcweb.utils.http import default_client + +BASE_URL = RX_BUILD_BACKEND.rstrip("/") + + +async def fetch_all_templates() -> list[dict]: + client = default_client() + async with client.get( + f"{BASE_URL.rstrip('/')}/v1/flexgen/templates/details" + ) as resp: + resp.raise_for_status() + return await resp.json() + + +async def fetch_template_detail(template_id: str) -> dict: + client = default_client() + async with client.get( + f"{BASE_URL.rstrip('/')}/v1/flexgen/templates/{template_id}" + ) as resp: + resp.raise_for_status() + return await resp.json() diff --git a/pcweb/pages/templates/templates_state.py b/pcweb/pages/templates/templates_state.py new file mode 100644 index 000000000..2ed894a29 --- /dev/null +++ b/pcweb/pages/templates/templates_state.py @@ -0,0 +1,268 @@ +import dataclasses +import json +import uuid +from typing import TypedDict + +import reflex as rx +from reflex.utils import console + +from pcweb.constants import REFLEX_BUILD_URL, REFLEX_DOMAIN_URL +from pcweb.pages.templates.templates_client import ( + fetch_all_templates, + fetch_template_detail, +) + + +@dataclasses.dataclass +class Template: + id: str + name: str + slug: str = "" + url: str | None = None + priority: int = 0 + tags: list[str] = dataclasses.field(default_factory=list) + enabled_integrations: list[str] = dataclasses.field(default_factory=list) + difficulty: str | None = None + description: str | None = None + about: str | None = None + requirements: list[str] | None = None + key_features: list[str] | None = None + tech_stack: list[str] | None = None + faq: list[dict[str, str]] | None = None + quotes: list[dict[str, str]] | None = None + prompt: str | None = None + last_modified: str | None = None + + +class TagWithCount(TypedDict): + label: str + count: int + + +def template_name_slug(raw_name: str) -> str: + """URL path segment: lowercase, spaces and underscores become hyphens.""" + return "-".join(raw_name.lower().replace("_", " ").split()) + + +def _parse_template(data: dict) -> Template: + raw_name = str(data.get("name", "")) + return Template( + id=str(data.get("id", "")), + name=raw_name.replace("_", " ").title(), + slug=template_name_slug(raw_name) or "template", + url=data.get("url"), + priority=data.get("priority", 0), + tags=data.get("tags") or [], + enabled_integrations=data.get("enabled_integrations") or [], + difficulty=data.get("difficulty") or "beginner", + description=data.get("description"), + about=data.get("about"), + requirements=data.get("requirements") or ["Python 3.10+"], + key_features=data.get("key_features"), + tech_stack=data.get("tech_stack") or ["Reflex"], + faq=data.get("faq"), + quotes=data.get("quotes"), + prompt=data.get("prompt"), + last_modified=str(data["last_modified"]) if data.get("last_modified") else None, + ) + + +def _parse_templates_map(data: list[dict]) -> dict[str, Template]: + return {str(item.get("id", "")): _parse_template(item) for item in data} + + +def _compute_tags(templates: list[Template]) -> list[TagWithCount]: + tag_counts: dict[str, int] = {} + for template in templates: + for tag in template.tags: + tag_counts[tag] = tag_counts.get(tag, 0) + 1 + return sorted( + [{"label": tag, "count": n} for tag, n in tag_counts.items()], + key=lambda x: (-x["count"], x["label"]), + ) + + +class TemplatesState(rx.State): + _all_templates: rx.Field[dict[str, Template]] = rx.field(default_factory=dict) + active_template: rx.Field[Template | None] = rx.field(default=None) + related_templates: rx.Field[list[Template]] = rx.field(default_factory=list) + query: rx.Field[str] = rx.field(default="") + is_loading: rx.Field[bool] = rx.field(default=False) + tags: rx.Field[list[TagWithCount]] = rx.field(default_factory=list) + checked_tags: rx.Field[set[str]] = rx.field(default_factory=set) + + def _matches_query(self, template: Template) -> bool: + query = self.query.strip().lower() + if not query: + return True + return ( + query in template.name.lower() + or query in (template.description or "").lower() + ) + + def _matches_tags(self, template: Template) -> bool: + if not template.tags: + return True + if not self.checked_tags: + return False + return bool(set(template.tags) & self.checked_tags) + + @rx.var + def faq_jsonld(self) -> str: + t = self.active_template + if not t or not t.faq: + return "" + data = { + "@context": "https://schema.org", + "@graph": [ + { + "@type": "WebPage", + "name": t.name, + "description": t.description or "", + "url": f"{REFLEX_DOMAIN_URL.rstrip('/')}/templates/{t.slug}/{t.id}", + }, + { + "@type": "FAQPage", + "mainEntity": [ + { + "@type": "Question", + "name": item["question"], + "acceptedAnswer": { + "@type": "Answer", + "text": item["answer"], + }, + } + for item in t.faq + ], + }, + ], + } + return json.dumps(data) + + @rx.var + def filtered_templates(self) -> list[Template]: + return [ + template + for template in self._all_templates.values() + if self._matches_query(template) and self._matches_tags(template) + ] + + @rx.event(background=True) + async def load_templates(self): + async with self: + if self._all_templates: + self.query = "" + self.tags = _compute_tags(list(self._all_templates.values())) + self.checked_tags = {t["label"] for t in self.tags} + return + self.query = "" + self.checked_tags = set() + async with self: + self.is_loading = True + try: + data = await fetch_all_templates() + templates = _parse_templates_map(data) + tags = _compute_tags(list(templates.values())) + + async with self: + self._all_templates = templates + self.tags = tags + self.checked_tags = {t["label"] for t in tags} + except Exception as e: + console.error(f"Failed to load templates: {e}") + finally: + async with self: + self.is_loading = False + + @rx.event(background=True) + async def prefetch_template(self, template_id: str): + try: + uuid.UUID(template_id) + except ValueError: + return + + async with self: + active = self.active_template + on_detail_page = "template_id" in self.router._page.params + + if on_detail_page or (active and active.id == template_id): + return + + try: + data = await fetch_template_detail(template_id) + template = _parse_template(data) + async with self: + self.active_template = template + except Exception as e: + console.error(f"Prefetch failed for {template_id}: {e}") + + @rx.event(background=True) + async def load_template_details(self, template_id: str = ""): + async with self: + template_id = template_id or self.router._page.params.get("template_id", "") + active = self.active_template + all_templates = dict(self._all_templates) + + if not template_id: + async with self: + self.active_template = None + self.related_templates = [] + return + + try: + uuid.UUID(template_id) + except ValueError: + async with self: + self.active_template = None + self.related_templates = [] + return + + try: + if active and active.id == template_id: + template = active + else: + data = await fetch_template_detail(template_id) + template = _parse_template(data) + + if not all_templates: + all_data = await fetch_all_templates() + all_templates = _parse_templates_map(all_data) + + all_templates[template_id] = template + + active_tags = set(template.tags) + others = [t for t in all_templates.values() if t.id != template_id] + others.sort(key=lambda t: -len(active_tags & set(t.tags))) + related = others[:3] + + async with self: + self.active_template = template + self.related_templates = related + self._all_templates = all_templates + except Exception as e: + console.error(f"Failed to load template {template_id}: {e}") + + @rx.event(debounce=300, temporal=True) + def set_query(self, value: str): + self.query = value + + @rx.event + def toggle_tag(self, value: str, checked: bool): + if checked: + self.checked_tags.add(value) + else: + self.checked_tags.discard(value) + + @rx.event + def redirect_to_template(self, template_id: str, use_prompt: bool = False): + template = self._all_templates.get(template_id) + if not template: + return + if use_prompt and template.prompt: + return rx.redirect( + f"{REFLEX_BUILD_URL.rstrip('/')}/?prompt={template.prompt}", + is_external=True, + ) + return rx.redirect( + f"{REFLEX_BUILD_URL.rstrip('/')}/gen/{template_id}", is_external=True + ) diff --git a/pcweb/pages/templates/views/__init__.py b/pcweb/pages/templates/views/__init__.py new file mode 100644 index 000000000..f50b3e4c3 --- /dev/null +++ b/pcweb/pages/templates/views/__init__.py @@ -0,0 +1,3 @@ +from pcweb.pages.templates.views.templates_grid import templates_grid, templates_sidebar + +__all__ = ["templates_grid", "templates_sidebar"] diff --git a/pcweb/pages/templates/views/templates_grid.py b/pcweb/pages/templates/views/templates_grid.py new file mode 100644 index 000000000..e00f10b6d --- /dev/null +++ b/pcweb/pages/templates/views/templates_grid.py @@ -0,0 +1,196 @@ +import reflex as rx +import reflex_ui as ui + +from pcweb.components.hosting_banner import HostingBannerState +from pcweb.components.marketing_button import button as marketing_button +from pcweb.constants import REFLEX_ASSETS_CDN, SCREENSHOT_BUCKET +from pcweb.pages.templates.templates_state import TagWithCount, Template, TemplatesState + + +def search_bar() -> rx.Component: + return rx.el.div( + ui.icon( + "Search01Icon", + ), + rx.el.input( + placeholder="Search templates...", + default_value=TemplatesState.query, + type="text", + on_change=TemplatesState.set_query, + class_name="outline-none focus:outline-none w-full", + ), + class_name="flex flex-row items-center gap-2 bg-white-1 px-2.5 rounded-[0.625rem] relative [box-shadow:0_-1px_0_0_rgba(0,_0,_0,_0.08)_inset,_0_0_0_1px_rgba(0,_0,_0,_0.08)_inset,_0_1px_2px_0_rgba(0,_0,_0,_0.02),_0_1px_4px_0_rgba(0,_0,_0,_0.02)] text-sm placeholder:text-secondary-9 font-[475] focus:outline-none outline-none dark:border border-secondary-a6 h-9", + ) + + +def checkbox_item(item: TagWithCount) -> rx.Component: + label = item["label"] + count = item["count"] + return ui.checkbox.root( + rx.el.div( + ui.checkbox.indicator(class_name="text-secondary-12"), + class_name="flex size-5 items-center justify-center rounded-sm bg-white-1 dark:bg-secondary-1 hover:bg-secondary-3 border border-secondary-a4", + ), + rx.el.span( + label, + class_name="text-secondary-11 text-sm font-[525] min-w-0 truncate group-hover:text-secondary-12 capitalize", + ), + rx.el.span( + count, + class_name="text-secondary-11 text-xs font-mono tabular-nums font-[415] ml-auto", + ), + key=label, + unstyled=True, + checked=TemplatesState.checked_tags.contains(label), + on_checked_change=lambda checked: TemplatesState.toggle_tag(label, checked), + class_name="flex flex-row items-center gap-3 min-h-8 min-w-0 cursor-pointer group", + ) + + +def categories_checkboxes() -> rx.Component: + return rx.el.div( + rx.el.span( + "CATEGORY", + class_name="text-secondary-12 text-xs font-[525] font-mono py-1", + ), + rx.el.div( + rx.foreach(TemplatesState.tags, checkbox_item), + class_name="flex flex-col gap-1", + ), + class_name="flex flex-col gap-4", + ) + + +def image_text_placeholder() -> rx.Component: + return rx.el.div( + rx.el.div( + rx.image( + src=f"{REFLEX_ASSETS_CDN}/logos/{rx.color_mode_cond(light='light', dark='dark')}/reflex.svg", + class_name="h-3.5 lg:h-5 w-auto opacity-70 dark:opacity-85 group-hover:scale-105 duration-200 ease-out", + alt="Logo", + ), + class_name="flex flex-row items-center justify-center h-full w-full rounded-[12px] relative overflow-hidden p-4", + ), + class_name="absolute inset-0 h-full w-full bg-white-1", + ) + + +@rx.memo +def template_card(template: Template) -> rx.Component: + return rx.el.div( + rx.el.div( + rx.el.div( + image_text_placeholder(), + ui.avatar.root( + ui.avatar.image( + src=f"{SCREENSHOT_BUCKET.strip('/')}/{template.id}", + loading="lazy", + ), + unstyled=True, + class_name="group-hover:scale-105 duration-200 ease-out object-top object-cover absolute inset-0 size-full transition-all", + ), + class_name="relative h-[12.25rem] overflow-hidden rounded-t-lg", + ), + class_name="flex-shrink-0 px-2 pt-2", + ), + rx.el.div( + rx.el.span( + template.name, + class_name="text-secondary-12 text-base font-[525] mb-2", + ), + rx.el.span( + template.description, + class_name="text-secondary-10 text-sm font-medium mb-4", + ), + rx.el.div( + rx.foreach( + template.tags, + lambda tag: rx.el.span( + f"#{tag}", + class_name="text-secondary-10 text-xs font-[525] capitalize", + ), + ), + class_name="flex flex-wrap gap-4 empty:hidden mb-4", + ), + marketing_button( + "Use Template", + variant="outline", + native_button=False, + on_click=TemplatesState.redirect_to_template( + template_id=template.id, + use_prompt=True, + ).stop_propagation, + class_name="relative z-10 mt-auto dark:hover:bg-secondary-2", + ), + class_name="p-6 flex flex-col h-full", + ), + rx.el.a( + href=f"/templates/{template.slug}/{template.id}", + class_name="absolute inset-0", + ), + on_mouse_enter=TemplatesState.prefetch_template(template.id), + key=template.id, + class_name="flex flex-col bg-white-1 dark:bg-m-slate-11 border-secondary-a4 dark:border shadow-[0_1px_0_0_rgba(0,_0,_0,_0.04),0_1px_1px_0_rgba(0,_0,_0,_0.01),0_4px_8px_0_rgba(0,_0,_0,_0.03)] rounded-xl border overflow-hidden isolate relative cursor-pointer group", + ) + + +def templates_sidebar() -> rx.Component: + return rx.box( + rx.el.div( + search_bar(), + categories_checkboxes(), + class_name=ui.cn( + "flex flex-col gap-8 pr-6 py-8 2xl:pl-0 pl-6 overflow-y-auto sticky", + rx.cond( + HostingBannerState.is_banner_visible, + "top-[113px] h-[calc(100vh-113px)]", + "top-[77px] h-[calc(100vh-77px)]", + ), + ), + ), + class_name=( + "w-[16.5rem] shrink-0 hidden lg:block z-10 border-r border-secondary-a4 relative " + "before:content-[''] before:absolute before:top-0 before:bottom-0 before:right-0 before:w-[100vw] before:bg-white-1 dark:before:bg-secondary-1 before:-z-10 max-xl:hidden" + ), + ) + + +def templates_grid() -> rx.Component: + return rx.el.div( + rx.el.div( + rx.el.h1( + "Templates", + class_name="text-4xl font-[575] text-secondary-12", + ), + rx.el.p( + "Start building faster with production-ready Reflex templates.", + class_name="text-secondary-11 text-sm font-medium", + ), + class_name="flex flex-col gap-4", + ), + rx.cond( + TemplatesState.is_loading, + rx.el.div( + ui.spinner(), + rx.el.span( + "Loading templates...", + ), + class_name="flex flex-row items-center gap-2 text-sm font-[525] text-secondary-11", + ), + rx.el.div( + rx.cond( + TemplatesState.is_hydrated, + rx.el.span( + "No templates found for the current filters.", + class_name="text-secondary-11 text-sm font-medium only:block hidden", + ), + ), + rx.foreach( + TemplatesState.filtered_templates, + lambda t: template_card(template=t), + ), + class_name="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 2xl:grid-cols-4 gap-x-4 gap-y-5", + ), + ), + class_name="flex flex-col gap-12 flex-1 min-w-0 lg:px-14 px-4 lg:py-16 py-10", + ) diff --git a/pcweb/pcweb.py b/pcweb/pcweb.py index 0d0724eb8..c272e6fac 100644 --- a/pcweb/pcweb.py +++ b/pcweb/pcweb.py @@ -81,6 +81,7 @@ "meta": [ {"name": "theme-color", "content": route.background_color}, ], + "on_load": route.on_load, } # Add the description only if it is not None diff --git a/pcweb/route.py b/pcweb/route.py index e79892865..7471ed0f2 100644 --- a/pcweb/route.py +++ b/pcweb/route.py @@ -1,20 +1,22 @@ """Manage routing for the application.""" +import dataclasses import inspect -from typing import Callable +from collections.abc import Callable import reflex as rx -from reflex.base import Base +from reflex.event import EventType -class Route(Base): +@dataclasses.dataclass(kw_only=True) +class Route: """A page route.""" # The path of the route. path: str # The page title. - title: str | None = None + title: str | rx.Var | None = None # The page description. description: str | None = None @@ -36,9 +38,8 @@ class Route(Base): # https://github.com/reflex-dev/reflex-web/pull/659#pullrequestreview-2021171902 add_as_page: bool = True - def __hash__(self): - """Hash the route based on the path and title.""" - return hash(f"{self.path}-{self.title}") + # The on_load function to call when the page is loaded. + on_load: EventType[()] | None = None def get_path(component_fn: Callable): diff --git a/pcweb/templates/secondary_page.py b/pcweb/templates/secondary_page.py new file mode 100644 index 000000000..44afc9a9b --- /dev/null +++ b/pcweb/templates/secondary_page.py @@ -0,0 +1,99 @@ +import functools +from typing import Callable + +import reflex as rx +import reflex_ui as ui +from reflex.event import EventType + +from pcweb.components.hosting_banner import HostingBannerState +from pcweb.route import Route + +DEFAULT_TITLE = "The platform to build and scale enterprise apps" +DEFAULT_DESCRIPTION = "Build secure internal apps with AI. Deploy on prem or cloud with governance. Technical and nontechnical teams ship together." + + +def secondary_page( + path: str, + title: str = DEFAULT_TITLE, + description: str | None = DEFAULT_DESCRIPTION, + image: str | None = None, + meta: list[dict[str, str]] | None = None, + props=None, + add_as_page=True, + on_load: EventType[()] | None = None, +) -> Callable: + """A template that most pages on the reflex.dev site should use. + + This template wraps the webpage with the navbar and footer. + + Args: + path: The path of the page. + title: The title of the page. + description: The description of the page. + image: The image to use for social media. + meta: Additional meta tags to add to the page. + props: Props to apply to the template. + add_as_page: whether to add the route to the app pages. + on_load: The function to call when the page is loaded. + + Returns: + A wrapper function that returns the full webpage. + """ + props = props or {} + + def secondary_page(contents: Callable[[], Route]) -> Route: + """Wrapper to create a templated route. + + Args: + contents: The function to create the page route. + + Returns: + The templated route. + """ + + @functools.wraps(contents) + def wrapper(*children, **props) -> rx.Component: + """The template component. + + Args: + children: The children components. + props: The props to apply to the component. + + Returns: + The component with the template applied. + """ + # Import here to avoid circular imports. + from pcweb.views.secondary_navbar import secondary_navbar + + # Wrap the component in the template. + return rx.el.div( + secondary_navbar(), + rx.el.main( + rx.el.div( + contents(*children, **props), + class_name="flex flex-col relative justify-center items-center w-full", + ), + class_name=ui.cn( + "flex flex-col w-full relative h-full justify-center items-center", + rx.cond( + HostingBannerState.is_banner_visible, + "mt-28", + "mt-16", + ), + ), + ), + class_name="flex flex-col w-full justify-center items-center relative dark:bg-m-slate-12 bg-m-slate-1", + ) + + return Route( + path=path, + title=title, + description=description, + image=image, + meta=meta, + component=wrapper, + add_as_page=add_as_page, + on_load=on_load, + ) + + return secondary_page diff --git a/pcweb/utils/http.py b/pcweb/utils/http.py new file mode 100644 index 000000000..5ee033d5a --- /dev/null +++ b/pcweb/utils/http.py @@ -0,0 +1,48 @@ +"""HTTP client utilities.""" + +from collections.abc import Callable +from functools import wraps +from typing import TypeVar + +import aiohttp +import orjson + +T = TypeVar("T") + + +def once(f: Callable[[], T]) -> Callable[[], T]: + """A decorator that calls the function once and caches the result. + + Args: + f: The function to call. + + Returns: + A function that calls the function once and caches the result. + """ + unset = object() + value: object | T = unset + + @wraps(f) + def wrapper() -> T: + nonlocal value + value = f() if value is unset else value + return value # pyright: ignore[reportReturnType] + + return wrapper + + +def json_dumps(obj: object) -> str: + return orjson.dumps(obj).decode("utf-8") + + +def one_client() -> aiohttp.ClientSession: + """Create a single aiohttp client session.""" + return aiohttp.ClientSession( + json_serialize=json_dumps, + ) + + +@once +def default_client() -> aiohttp.ClientSession: + """Create a default aiohttp client session.""" + return one_client() diff --git a/pcweb/views/marketing_navbar.py b/pcweb/views/marketing_navbar.py index a90489379..61c656e45 100644 --- a/pcweb/views/marketing_navbar.py +++ b/pcweb/views/marketing_navbar.py @@ -104,7 +104,7 @@ def logo() -> rx.Component: class_name="shrink-0 hidden dark:block", ), to="/", - class_name="block shrink-0 mr-9", + class_name="block shrink-0 lg:mr-9", ) diff --git a/pcweb/views/secondary_navbar.py b/pcweb/views/secondary_navbar.py new file mode 100644 index 000000000..185f68888 --- /dev/null +++ b/pcweb/views/secondary_navbar.py @@ -0,0 +1,172 @@ +import reflex as rx +import reflex_ui as ui +from reflex_ui.blocks.demo_form import demo_form_dialog + +from pcweb.components.docpage.navbar.buttons.sidebar import navbar_sidebar_button +from pcweb.components.marketing_button import button +from pcweb.constants import REFLEX_BUILD_URL +from pcweb.views.marketing_navbar import ( + about_content, + github, + logo, + menu_trigger, + platform_content, + resources_content, + solutions_content, +) + + +def menu_item(text: str, href: str, active_str: str = "") -> rx.Component: + router_path = rx.State.router.page.path + active_cn = "shadow-[inset_0_-1px_0_0_var(--primary-10)] [&_button]:text-primary-10 [&_div]:text-primary-10" + + # For paths starting with "/" (like Start), use exact match + # For "framework", it's the default - active when in /docs but not matching other sections + # For other segments (like "ai-builder"), use contains + if active_str.startswith("/"): + active = router_path == active_str + elif active_str == "framework": + # Framework is active when in /docs but not in other specific sections + is_docs = router_path.contains("/docs") + is_ai_builder = router_path.contains("ai-builder") + is_enterprise = router_path.contains("enterprise") + is_hosting = router_path.contains("hosting") + is_start = router_path == "/docs" + active = is_docs & ~is_ai_builder & ~is_enterprise & ~is_hosting & ~is_start + else: + active = router_path.contains(active_str) + + return ui.navigation_menu.item( + rx.el.a( + button( + text, + size="sm", + variant="ghost", + native_button=False, + ), + to=href, + ), + class_name=ui.cn( + "xl:flex hidden h-full items-center justify-center", + rx.cond(active, active_cn, ""), + ), + custom_attrs={"role": "menuitem"}, + ) + + +def navigation_menu() -> rx.Component: + return ui.navigation_menu.root( + ui.navigation_menu.list( + menu_trigger("Platform", platform_content()), + menu_trigger("Solutions", solutions_content()), + menu_trigger("Resources", resources_content()), + ui.navigation_menu.item( + rx.el.a( + button( + "Pricing", + size="sm", + variant="ghost", + native_button=False, + ), + to="/pricing", + ), + class_name="xl:flex hidden px-1", + custom_attrs={"role": "menuitem"}, + ), + ui.navigation_menu.item( + rx.el.a( + button( + "Docs", + size="sm", + variant="ghost", + ), + to="/docs", + ), + class_name="xl:flex hidden px-1", + custom_attrs={"role": "menuitem"}, + ), + menu_trigger("About", about_content()), + class_name="flex flex-row items-center m-0 h-full list-none", + custom_attrs={"role": "menubar"}, + ), + ui.navigation_menu.list( + ui.navigation_menu.item( + github(), + custom_attrs={"role": "menuitem"}, + ), + ui.navigation_menu.item( + rx.el.a( + button( + "Sign In", + ui.icon("Login01Icon", class_name="scale-x-[-1]"), + size="sm", + variant="outline", + native_button=False, + ), + to=f"{REFLEX_BUILD_URL.strip('/')}/cloud-login?redirect_to={REFLEX_BUILD_URL.strip('/')}/callback/", + target="_blank", + ), + custom_attrs={"role": "menuitem"}, + ), + ui.navigation_menu.item( + demo_form_dialog( + trigger=button( + "Book a Demo", + size="sm", + variant="primary", + class_name=" whitespace-nowrap max-xl:hidden", + native_button=False, + ), + ), + unstyled=True, + class_name="xl:flex hidden", + custom_attrs={"role": "menuitem"}, + ), + ui.navigation_menu.item( + navbar_sidebar_button(), + class_name="xl:hidden flex", + unstyled=True, + custom_attrs={"role": "menuitem"}, + ), + class_name="flex flex-row lg:gap-4 gap-2 m-0 h-full list-none items-center", + custom_attrs={"role": "menubar"}, + ), + ui.navigation_menu.portal( + ui.navigation_menu.positioner( + ui.navigation_menu.popup( + ui.navigation_menu.viewport( + unstyled=True, + class_name="relative h-full w-full overflow-hidden rounded-[inherit]", + ), + unstyled=True, + class_name="relative h-[var(--popup-height)] w-[var(--popup-width)] origin-[var(--transform-origin)] rounded-xl bg-m-slate-1 dark:bg-m-slate-12 navbar-shadow transition-[opacity,transform,width,height,scale,translate] duration-150 ease-[cubic-bezier(0.22,1,0.36,1)] data-[ending-style]:ease-[ease] data-[ending-style]:scale-90 data-[ending-style]:opacity-0 data-[ending-style]:duration-150 data-[starting-style]:scale-90 data-[starting-style]:opacity-0", + ), + unstyled=True, + class_name="safari-nav-positioner box-border h-[var(--positioner-height)] w-[var(--positioner-width)] max-w-[var(--available-width)] transition-[top,left,right,bottom] duration-[0.35s] ease-[cubic-bezier(0.22,1,0.36,1)] data-[instant]:transition-none", + side_offset=30, + align="start", + align_offset=-20, + position_method="fixed", + ), + ), + unstyled=True, + class_name="relative flex w-full items-center h-full justify-between gap-6 mx-auto flex-row", + ) + + +@rx.memo +def secondary_navbar() -> rx.Component: + from pcweb.components.hosting_banner import hosting_banner + + return rx.el.div( + hosting_banner(), + rx.el.header( + rx.el.div( + logo(), + navigation_menu(), + class_name="relative flex w-full items-center h-full justify-between gap-6 mx-auto flex-row max-w-[108rem]", + ), + class_name="w-full max-full h-[4.5rem] mx-auto flex flex-row items-center 3xl:px-16 px-6 backdrop-blur-[16px] shadow-[0_-2px_2px_1px_rgba(0,0,0,0.02),0_1px_1px_0_rgba(0,0,0,0.08),0_4px_8px_0_rgba(0,0,0,0.03),0_0_0_1px_#FFF_inset] dark:shadow-none dark:border-b dark:border-m-slate-10 bg-gradient-to-b from-white to-m-slate-1 dark:from-m-slate-11 dark:to-m-slate-12", + ), + class_name="flex flex-col w-full top-0 z-[9999] fixed self-center", + ) diff --git a/pyproject.toml b/pyproject.toml index 16756f9bf..0b9fcd587 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,6 +14,7 @@ dependencies = [ "googletrans-py", "mistletoe", "openai", + "orjson", "pandas", "plotly-express", "psycopg[binary]", @@ -25,7 +26,6 @@ dependencies = [ "reflex-image-zoom", "reflex-pyplot", "reflex-ui @ git+https://github.com/reflex-dev/reflex-ui@main", - "replicate", "requests", "ruff-format", "typesense", diff --git a/tests/test_routes.py b/tests/test_routes.py new file mode 100644 index 000000000..45d9c0668 --- /dev/null +++ b/tests/test_routes.py @@ -0,0 +1,32 @@ +"""Integration tests for all routes in Reflex.""" + +import sys +from collections import Counter +from pathlib import Path + +import pytest + +sys.path.append(str(Path(__file__).resolve().parent.parent)) + + +@pytest.fixture +def routes_fixture(): + from pcweb.pages import routes + + yield routes + + +def test_unique_routes(routes_fixture): + assert routes_fixture is not None + + paths = [route.path for route in routes_fixture if route.path] + + # Count occurrences of each path + path_counts = Counter(paths) + # Find duplicate paths + duplicates = {path: count for path, count in path_counts.items() if count > 1} + + # Assert that there are no duplicates + assert len(duplicates) == 0, f"Duplicate routes found: {duplicates}" + + print(f"Test passed. All {len(paths)} routes are unique.") diff --git a/tests/test_titles.py b/tests/test_titles.py deleted file mode 100644 index 3e7eb3bda..000000000 --- a/tests/test_titles.py +++ /dev/null @@ -1,32 +0,0 @@ -"""Integration tests for all titles in Reflex.""" - -import sys -from collections import Counter -from pathlib import Path - -import pytest - -sys.path.append(str(Path(__file__).resolve().parent.parent)) - - -@pytest.fixture -def routes_fixture(): - from pcweb.pages import routes - - yield routes - - -def test_unique_titles(routes_fixture): - assert routes_fixture is not None - - titles = [route for route in routes_fixture if hasattr(route, "title")] - - # Count occurrences of each title - title_counts = Counter(titles) - # Find duplicate titles - duplicates = [title for title, count in title_counts.items() if count > 1] - - # Assert that there are no duplicates - assert len(duplicates) == 0, f"Duplicate titles found: {duplicates}" - - print(f"Test passed. All {len(titles)} titles are unique.") diff --git a/uv.lock b/uv.lock index 403ced033..bd0a69815 100644 --- a/uv.lock +++ b/uv.lock @@ -203,11 +203,11 @@ wheels = [ [[package]] name = "attrs" -version = "25.4.0" +version = "26.1.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6b/5c/685e6633917e101e5dcb62b9dd76946cbb57c26e133bae9e0cd36033c0a9/attrs-25.4.0.tar.gz", hash = "sha256:16d5969b87f0859ef33a48b35d55ac1be6e42ae49d5e853b597db70c35c57e11", size = 934251, upload-time = "2025-10-06T13:54:44.725Z" } +sdist = { url = "https://files.pythonhosted.org/packages/9a/8e/82a0fe20a541c03148528be8cac2408564a6c9a0cc7e9171802bc1d26985/attrs-26.1.0.tar.gz", hash = "sha256:d03ceb89cb322a8fd706d4fb91940737b6642aa36998fe130a9bc96c985eff32", size = 952055, upload-time = "2026-03-19T14:22:25.026Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/3a/2a/7cc015f5b9f5db42b7d48157e23356022889fc354a2813c15934b7cb5c0e/attrs-25.4.0-py3-none-any.whl", hash = "sha256:adcf7e2a1fb3b36ac48d97835bb6d8ade15b8dcce26aba8bf1d14847b57a3373", size = 67615, upload-time = "2025-10-06T13:54:43.17Z" }, + { url = "https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl", hash = "sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309", size = 67548, upload-time = "2026-03-19T14:22:23.645Z" }, ] [[package]] @@ -609,7 +609,7 @@ wheels = [ [[package]] name = "fastapi" -version = "0.135.1" +version = "0.135.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "annotated-doc" }, @@ -618,9 +618,9 @@ dependencies = [ { name = "typing-extensions" }, { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e7/7b/f8e0211e9380f7195ba3f3d40c292594fd81ba8ec4629e3854c353aaca45/fastapi-0.135.1.tar.gz", hash = "sha256:d04115b508d936d254cea545b7312ecaa58a7b3a0f84952535b4c9afae7668cd", size = 394962, upload-time = "2026-03-01T18:18:29.369Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c4/73/5903c4b13beae98618d64eb9870c3fac4f605523dd0312ca5c80dadbd5b9/fastapi-0.135.2.tar.gz", hash = "sha256:88a832095359755527b7f63bb4c6bc9edb8329a026189eed83d6c1afcf419d56", size = 395833, upload-time = "2026-03-23T14:12:41.697Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e4/72/42e900510195b23a56bde950d26a51f8b723846bfcaa0286e90287f0422b/fastapi-0.135.1-py3-none-any.whl", hash = "sha256:46e2fc5745924b7c840f71ddd277382af29ce1cdb7d5eab5bf697e3fb9999c9e", size = 116999, upload-time = "2026-03-01T18:18:30.831Z" }, + { url = "https://files.pythonhosted.org/packages/8f/ea/18f6d0457f9efb2fc6fa594857f92810cadb03024975726db6546b3d6fcf/fastapi-0.135.2-py3-none-any.whl", hash = "sha256:0af0447d541867e8db2a6a25c23a8c4bd80e2394ac5529bd87501bbb9e240ca5", size = 117407, upload-time = "2026-03-23T14:12:43.284Z" }, ] [[package]] @@ -1650,7 +1650,7 @@ wheels = [ [[package]] name = "openai" -version = "2.28.0" +version = "2.29.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, @@ -1662,9 +1662,77 @@ dependencies = [ { name = "tqdm" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/56/87/eb0abb4ef88ddb95b3c13149384c4c288f584f3be17d6a4f63f8c3e3c226/openai-2.28.0.tar.gz", hash = "sha256:bb7fdff384d2a787fa82e8822d1dd3c02e8cf901d60f1df523b7da03cbb6d48d", size = 670334, upload-time = "2026-03-13T19:56:27.306Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c0/5a/df122348638885526e53140e9c6b0d844af7312682b3bde9587eebc28b47/openai-2.28.0-py3-none-any.whl", hash = "sha256:79aa5c45dba7fef84085701c235cf13ba88485e1ef4f8dfcedc44fc2a698fc1d", size = 1141218, upload-time = "2026-03-13T19:56:25.46Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/b4/15/203d537e58986b5673e7f232453a2a2f110f22757b15921cbdeea392e520/openai-2.29.0.tar.gz", hash = "sha256:32d09eb2f661b38d3edd7d7e1a2943d1633f572596febe64c0cd370c86d52bec", size = 671128, upload-time = "2026-03-17T17:53:49.599Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d0/b1/35b6f9c8cf9318e3dbb7146cc82dab4cf61182a8d5406fc9b50864362895/openai-2.29.0-py3-none-any.whl", hash = "sha256:b7c5de513c3286d17c5e29b92c4c98ceaf0d775244ac8159aeb1bddf840eb42a", size = 1141533, upload-time = "2026-03-17T17:53:47.348Z" }, +] + +[[package]] +name = "orjson" +version = "3.11.7" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/53/45/b268004f745ede84e5798b48ee12b05129d19235d0e15267aa57dcdb400b/orjson-3.11.7.tar.gz", hash = "sha256:9b1a67243945819ce55d24a30b59d6a168e86220452d2c96f4d1f093e71c0c49", size = 6144992, upload-time = "2026-02-02T15:38:49.29Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/37/02/da6cb01fc6087048d7f61522c327edf4250f1683a58a839fdcc435746dd5/orjson-3.11.7-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:9487abc2c2086e7c8eb9a211d2ce8855bae0e92586279d0d27b341d5ad76c85c", size = 228664, upload-time = "2026-02-02T15:37:25.542Z" }, + { url = "https://files.pythonhosted.org/packages/c1/c2/5885e7a5881dba9a9af51bc564e8967225a642b3e03d089289a35054e749/orjson-3.11.7-cp311-cp311-macosx_15_0_arm64.whl", hash = "sha256:79cacb0b52f6004caf92405a7e1f11e6e2de8bdf9019e4f76b44ba045125cd6b", size = 125344, upload-time = "2026-02-02T15:37:26.92Z" }, + { url = "https://files.pythonhosted.org/packages/a4/1d/4e7688de0a92d1caf600dfd5fb70b4c5bfff51dfa61ac555072ef2d0d32a/orjson-3.11.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c2e85fe4698b6a56d5e2ebf7ae87544d668eb6bde1ad1226c13f44663f20ec9e", size = 128404, upload-time = "2026-02-02T15:37:28.108Z" }, + { url = "https://files.pythonhosted.org/packages/2f/b2/ec04b74ae03a125db7bd69cffd014b227b7f341e3261bf75b5eb88a1aa92/orjson-3.11.7-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b8d14b71c0b12963fe8a62aac87119f1afdf4cb88a400f61ca5ae581449efcb5", size = 123677, upload-time = "2026-02-02T15:37:30.287Z" }, + { url = "https://files.pythonhosted.org/packages/4c/69/f95bdf960605f08f827f6e3291fe243d8aa9c5c9ff017a8d7232209184c3/orjson-3.11.7-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:91c81ef070c8f3220054115e1ef468b1c9ce8497b4e526cb9f68ab4dc0a7ac62", size = 128950, upload-time = "2026-02-02T15:37:31.595Z" }, + { url = "https://files.pythonhosted.org/packages/a4/1b/de59c57bae1d148ef298852abd31909ac3089cff370dfd4cd84cc99cbc42/orjson-3.11.7-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:411ebaf34d735e25e358a6d9e7978954a9c9d58cfb47bc6683cdc3964cd2f910", size = 141756, upload-time = "2026-02-02T15:37:32.985Z" }, + { url = "https://files.pythonhosted.org/packages/ee/9e/9decc59f4499f695f65c650f6cfa6cd4c37a3fbe8fa235a0a3614cb54386/orjson-3.11.7-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a16bcd08ab0bcdfc7e8801d9c4a9cc17e58418e4d48ddc6ded4e9e4b1a94062b", size = 130812, upload-time = "2026-02-02T15:37:34.204Z" }, + { url = "https://files.pythonhosted.org/packages/28/e6/59f932bcabd1eac44e334fe8e3281a92eacfcb450586e1f4bde0423728d8/orjson-3.11.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c0b51672e466fd7e56230ffbae7f1639e18d0ce023351fb75da21b71bc2c960", size = 133444, upload-time = "2026-02-02T15:37:35.446Z" }, + { url = "https://files.pythonhosted.org/packages/f1/36/b0f05c0eaa7ca30bc965e37e6a2956b0d67adb87a9872942d3568da846ae/orjson-3.11.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:136dcd6a2e796dfd9ffca9fc027d778567b0b7c9968d092842d3c323cef88aa8", size = 138609, upload-time = "2026-02-02T15:37:36.657Z" }, + { url = "https://files.pythonhosted.org/packages/b8/03/58ec7d302b8d86944c60c7b4b82975d5161fcce4c9bc8c6cb1d6741b6115/orjson-3.11.7-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:7ba61079379b0ae29e117db13bda5f28d939766e410d321ec1624afc6a0b0504", size = 408918, upload-time = "2026-02-02T15:37:38.076Z" }, + { url = "https://files.pythonhosted.org/packages/06/3a/868d65ef9a8b99be723bd510de491349618abd9f62c826cf206d962db295/orjson-3.11.7-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:0527a4510c300e3b406591b0ba69b5dc50031895b0a93743526a3fc45f59d26e", size = 143998, upload-time = "2026-02-02T15:37:39.706Z" }, + { url = "https://files.pythonhosted.org/packages/5b/c7/1e18e1c83afe3349f4f6dc9e14910f0ae5f82eac756d1412ea4018938535/orjson-3.11.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a709e881723c9b18acddcfb8ba357322491ad553e277cf467e1e7e20e2d90561", size = 134802, upload-time = "2026-02-02T15:37:41.002Z" }, + { url = "https://files.pythonhosted.org/packages/d4/0b/ccb7ee1a65b37e8eeb8b267dc953561d72370e85185e459616d4345bab34/orjson-3.11.7-cp311-cp311-win32.whl", hash = "sha256:c43b8b5bab288b6b90dac410cca7e986a4fa747a2e8f94615aea407da706980d", size = 127828, upload-time = "2026-02-02T15:37:42.241Z" }, + { url = "https://files.pythonhosted.org/packages/af/9e/55c776dffda3f381e0f07d010a4f5f3902bf48eaba1bb7684d301acd4924/orjson-3.11.7-cp311-cp311-win_amd64.whl", hash = "sha256:6543001328aa857187f905308a028935864aefe9968af3848401b6fe80dbb471", size = 124941, upload-time = "2026-02-02T15:37:43.444Z" }, + { url = "https://files.pythonhosted.org/packages/aa/8e/424a620fa7d263b880162505fb107ef5e0afaa765b5b06a88312ac291560/orjson-3.11.7-cp311-cp311-win_arm64.whl", hash = "sha256:1ee5cc7160a821dfe14f130bc8e63e7611051f964b463d9e2a3a573204446a4d", size = 126245, upload-time = "2026-02-02T15:37:45.18Z" }, + { url = "https://files.pythonhosted.org/packages/80/bf/76f4f1665f6983385938f0e2a5d7efa12a58171b8456c252f3bae8a4cf75/orjson-3.11.7-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:bd03ea7606833655048dab1a00734a2875e3e86c276e1d772b2a02556f0d895f", size = 228545, upload-time = "2026-02-02T15:37:46.376Z" }, + { url = "https://files.pythonhosted.org/packages/79/53/6c72c002cb13b5a978a068add59b25a8bdf2800ac1c9c8ecdb26d6d97064/orjson-3.11.7-cp312-cp312-macosx_15_0_arm64.whl", hash = "sha256:89e440ebc74ce8ab5c7bc4ce6757b4a6b1041becb127df818f6997b5c71aa60b", size = 125224, upload-time = "2026-02-02T15:37:47.697Z" }, + { url = "https://files.pythonhosted.org/packages/2c/83/10e48852865e5dd151bdfe652c06f7da484578ed02c5fca938e3632cb0b8/orjson-3.11.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5ede977b5fe5ac91b1dffc0a517ca4542d2ec8a6a4ff7b2652d94f640796342a", size = 128154, upload-time = "2026-02-02T15:37:48.954Z" }, + { url = "https://files.pythonhosted.org/packages/6e/52/a66e22a2b9abaa374b4a081d410edab6d1e30024707b87eab7c734afe28d/orjson-3.11.7-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b7b1dae39230a393df353827c855a5f176271c23434cfd2db74e0e424e693e10", size = 123548, upload-time = "2026-02-02T15:37:50.187Z" }, + { url = "https://files.pythonhosted.org/packages/de/38/605d371417021359f4910c496f764c48ceb8997605f8c25bf1dfe58c0ebe/orjson-3.11.7-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ed46f17096e28fb28d2975834836a639af7278aa87c84f68ab08fbe5b8bd75fa", size = 129000, upload-time = "2026-02-02T15:37:51.426Z" }, + { url = "https://files.pythonhosted.org/packages/44/98/af32e842b0ffd2335c89714d48ca4e3917b42f5d6ee5537832e069a4b3ac/orjson-3.11.7-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3726be79e36e526e3d9c1aceaadbfb4a04ee80a72ab47b3f3c17fefb9812e7b8", size = 141686, upload-time = "2026-02-02T15:37:52.607Z" }, + { url = "https://files.pythonhosted.org/packages/96/0b/fc793858dfa54be6feee940c1463370ece34b3c39c1ca0aa3845f5ba9892/orjson-3.11.7-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0724e265bc548af1dedebd9cb3d24b4e1c1e685a343be43e87ba922a5c5fff2f", size = 130812, upload-time = "2026-02-02T15:37:53.944Z" }, + { url = "https://files.pythonhosted.org/packages/dc/91/98a52415059db3f374757d0b7f0f16e3b5cd5976c90d1c2b56acaea039e6/orjson-3.11.7-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e7745312efa9e11c17fbd3cb3097262d079da26930ae9ae7ba28fb738367cbad", size = 133440, upload-time = "2026-02-02T15:37:55.615Z" }, + { url = "https://files.pythonhosted.org/packages/dc/b6/cb540117bda61791f46381f8c26c8f93e802892830a6055748d3bb1925ab/orjson-3.11.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f904c24bdeabd4298f7a977ef14ca2a022ca921ed670b92ecd16ab6f3d01f867", size = 138386, upload-time = "2026-02-02T15:37:56.814Z" }, + { url = "https://files.pythonhosted.org/packages/63/1a/50a3201c334a7f17c231eee5f841342190723794e3b06293f26e7cf87d31/orjson-3.11.7-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:b9fc4d0f81f394689e0814617aadc4f2ea0e8025f38c226cbf22d3b5ddbf025d", size = 408853, upload-time = "2026-02-02T15:37:58.291Z" }, + { url = "https://files.pythonhosted.org/packages/87/cd/8de1c67d0be44fdc22701e5989c0d015a2adf391498ad42c4dc589cd3013/orjson-3.11.7-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:849e38203e5be40b776ed2718e587faf204d184fc9a008ae441f9442320c0cab", size = 144130, upload-time = "2026-02-02T15:38:00.163Z" }, + { url = "https://files.pythonhosted.org/packages/0f/fe/d605d700c35dd55f51710d159fc54516a280923cd1b7e47508982fbb387d/orjson-3.11.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:4682d1db3bcebd2b64757e0ddf9e87ae5f00d29d16c5cdf3a62f561d08cc3dd2", size = 134818, upload-time = "2026-02-02T15:38:01.507Z" }, + { url = "https://files.pythonhosted.org/packages/e4/e4/15ecc67edb3ddb3e2f46ae04475f2d294e8b60c1825fbe28a428b93b3fbd/orjson-3.11.7-cp312-cp312-win32.whl", hash = "sha256:f4f7c956b5215d949a1f65334cf9d7612dde38f20a95f2315deef167def91a6f", size = 127923, upload-time = "2026-02-02T15:38:02.75Z" }, + { url = "https://files.pythonhosted.org/packages/34/70/2e0855361f76198a3965273048c8e50a9695d88cd75811a5b46444895845/orjson-3.11.7-cp312-cp312-win_amd64.whl", hash = "sha256:bf742e149121dc5648ba0a08ea0871e87b660467ef168a3a5e53bc1fbd64bb74", size = 125007, upload-time = "2026-02-02T15:38:04.032Z" }, + { url = "https://files.pythonhosted.org/packages/68/40/c2051bd19fc467610fed469dc29e43ac65891571138f476834ca192bc290/orjson-3.11.7-cp312-cp312-win_arm64.whl", hash = "sha256:26c3b9132f783b7d7903bf1efb095fed8d4a3a85ec0d334ee8beff3d7a4749d5", size = 126089, upload-time = "2026-02-02T15:38:05.297Z" }, + { url = "https://files.pythonhosted.org/packages/89/25/6e0e52cac5aab51d7b6dcd257e855e1dec1c2060f6b28566c509b4665f62/orjson-3.11.7-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:1d98b30cc1313d52d4af17d9c3d307b08389752ec5f2e5febdfada70b0f8c733", size = 228390, upload-time = "2026-02-02T15:38:06.8Z" }, + { url = "https://files.pythonhosted.org/packages/a5/29/a77f48d2fc8a05bbc529e5ff481fb43d914f9e383ea2469d4f3d51df3d00/orjson-3.11.7-cp313-cp313-macosx_15_0_arm64.whl", hash = "sha256:d897e81f8d0cbd2abb82226d1860ad2e1ab3ff16d7b08c96ca00df9d45409ef4", size = 125189, upload-time = "2026-02-02T15:38:08.181Z" }, + { url = "https://files.pythonhosted.org/packages/89/25/0a16e0729a0e6a1504f9d1a13cdd365f030068aab64cec6958396b9969d7/orjson-3.11.7-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:814be4b49b228cfc0b3c565acf642dd7d13538f966e3ccde61f4f55be3e20785", size = 128106, upload-time = "2026-02-02T15:38:09.41Z" }, + { url = "https://files.pythonhosted.org/packages/66/da/a2e505469d60666a05ab373f1a6322eb671cb2ba3a0ccfc7d4bc97196787/orjson-3.11.7-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d06e5c5fed5caedd2e540d62e5b1c25e8c82431b9e577c33537e5fa4aa909539", size = 123363, upload-time = "2026-02-02T15:38:10.73Z" }, + { url = "https://files.pythonhosted.org/packages/23/bf/ed73f88396ea35c71b38961734ea4a4746f7ca0768bf28fd551d37e48dd0/orjson-3.11.7-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:31c80ce534ac4ea3739c5ee751270646cbc46e45aea7576a38ffec040b4029a1", size = 129007, upload-time = "2026-02-02T15:38:12.138Z" }, + { url = "https://files.pythonhosted.org/packages/73/3c/b05d80716f0225fc9008fbf8ab22841dcc268a626aa550561743714ce3bf/orjson-3.11.7-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f50979824bde13d32b4320eedd513431c921102796d86be3eee0b58e58a3ecd1", size = 141667, upload-time = "2026-02-02T15:38:13.398Z" }, + { url = "https://files.pythonhosted.org/packages/61/e8/0be9b0addd9bf86abfc938e97441dcd0375d494594b1c8ad10fe57479617/orjson-3.11.7-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9e54f3808e2b6b945078c41aa8d9b5834b28c50843846e97807e5adb75fa9705", size = 130832, upload-time = "2026-02-02T15:38:14.698Z" }, + { url = "https://files.pythonhosted.org/packages/c9/ec/c68e3b9021a31d9ec15a94931db1410136af862955854ed5dd7e7e4f5bff/orjson-3.11.7-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a12b80df61aab7b98b490fe9e4879925ba666fccdfcd175252ce4d9035865ace", size = 133373, upload-time = "2026-02-02T15:38:16.109Z" }, + { url = "https://files.pythonhosted.org/packages/d2/45/f3466739aaafa570cc8e77c6dbb853c48bf56e3b43738020e2661e08b0ac/orjson-3.11.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:996b65230271f1a97026fd0e6a753f51fbc0c335d2ad0c6201f711b0da32693b", size = 138307, upload-time = "2026-02-02T15:38:17.453Z" }, + { url = "https://files.pythonhosted.org/packages/e1/84/9f7f02288da1ffb31405c1be07657afd1eecbcb4b64ee2817b6fe0f785fa/orjson-3.11.7-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:ab49d4b2a6a1d415ddb9f37a21e02e0d5dbfe10b7870b21bf779fc21e9156157", size = 408695, upload-time = "2026-02-02T15:38:18.831Z" }, + { url = "https://files.pythonhosted.org/packages/18/07/9dd2f0c0104f1a0295ffbe912bc8d63307a539b900dd9e2c48ef7810d971/orjson-3.11.7-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:390a1dce0c055ddf8adb6aa94a73b45a4a7d7177b5c584b8d1c1947f2ba60fb3", size = 144099, upload-time = "2026-02-02T15:38:20.28Z" }, + { url = "https://files.pythonhosted.org/packages/a5/66/857a8e4a3292e1f7b1b202883bcdeb43a91566cf59a93f97c53b44bd6801/orjson-3.11.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1eb80451a9c351a71dfaf5b7ccc13ad065405217726b59fdbeadbcc544f9d223", size = 134806, upload-time = "2026-02-02T15:38:22.186Z" }, + { url = "https://files.pythonhosted.org/packages/0a/5b/6ebcf3defc1aab3a338ca777214966851e92efb1f30dc7fc8285216e6d1b/orjson-3.11.7-cp313-cp313-win32.whl", hash = "sha256:7477aa6a6ec6139c5cb1cc7b214643592169a5494d200397c7fc95d740d5fcf3", size = 127914, upload-time = "2026-02-02T15:38:23.511Z" }, + { url = "https://files.pythonhosted.org/packages/00/04/c6f72daca5092e3117840a1b1e88dfc809cc1470cf0734890d0366b684a1/orjson-3.11.7-cp313-cp313-win_amd64.whl", hash = "sha256:b9f95dcdea9d4f805daa9ddf02617a89e484c6985fa03055459f90e87d7a0757", size = 124986, upload-time = "2026-02-02T15:38:24.836Z" }, + { url = "https://files.pythonhosted.org/packages/03/ba/077a0f6f1085d6b806937246860fafbd5b17f3919c70ee3f3d8d9c713f38/orjson-3.11.7-cp313-cp313-win_arm64.whl", hash = "sha256:800988273a014a0541483dc81021247d7eacb0c845a9d1a34a422bc718f41539", size = 126045, upload-time = "2026-02-02T15:38:26.216Z" }, + { url = "https://files.pythonhosted.org/packages/e9/1e/745565dca749813db9a093c5ebc4bac1a9475c64d54b95654336ac3ed961/orjson-3.11.7-cp314-cp314-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:de0a37f21d0d364954ad5de1970491d7fbd0fb1ef7417d4d56a36dc01ba0c0a0", size = 228391, upload-time = "2026-02-02T15:38:27.757Z" }, + { url = "https://files.pythonhosted.org/packages/46/19/e40f6225da4d3aa0c8dc6e5219c5e87c2063a560fe0d72a88deb59776794/orjson-3.11.7-cp314-cp314-macosx_15_0_arm64.whl", hash = "sha256:c2428d358d85e8da9d37cba18b8c4047c55222007a84f97156a5b22028dfbfc0", size = 125188, upload-time = "2026-02-02T15:38:29.241Z" }, + { url = "https://files.pythonhosted.org/packages/9d/7e/c4de2babef2c0817fd1f048fd176aa48c37bec8aef53d2fa932983032cce/orjson-3.11.7-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c4bc6c6ac52cdaa267552544c73e486fecbd710b7ac09bc024d5a78555a22f6", size = 128097, upload-time = "2026-02-02T15:38:30.618Z" }, + { url = "https://files.pythonhosted.org/packages/eb/74/233d360632bafd2197f217eee7fb9c9d0229eac0c18128aee5b35b0014fe/orjson-3.11.7-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bd0d68edd7dfca1b2eca9361a44ac9f24b078de3481003159929a0573f21a6bf", size = 123364, upload-time = "2026-02-02T15:38:32.363Z" }, + { url = "https://files.pythonhosted.org/packages/79/51/af79504981dd31efe20a9e360eb49c15f06df2b40e7f25a0a52d9ae888e8/orjson-3.11.7-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:623ad1b9548ef63886319c16fa317848e465a21513b31a6ad7b57443c3e0dcf5", size = 129076, upload-time = "2026-02-02T15:38:33.68Z" }, + { url = "https://files.pythonhosted.org/packages/67/e2/da898eb68b72304f8de05ca6715870d09d603ee98d30a27e8a9629abc64b/orjson-3.11.7-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6e776b998ac37c0396093d10290e60283f59cfe0fc3fccbd0ccc4bd04dd19892", size = 141705, upload-time = "2026-02-02T15:38:34.989Z" }, + { url = "https://files.pythonhosted.org/packages/c5/89/15364d92acb3d903b029e28d834edb8780c2b97404cbf7929aa6b9abdb24/orjson-3.11.7-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:652c6c3af76716f4a9c290371ba2e390ede06f6603edb277b481daf37f6f464e", size = 130855, upload-time = "2026-02-02T15:38:36.379Z" }, + { url = "https://files.pythonhosted.org/packages/c2/8b/ecdad52d0b38d4b8f514be603e69ccd5eacf4e7241f972e37e79792212ec/orjson-3.11.7-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a56df3239294ea5964adf074c54bcc4f0ccd21636049a2cf3ca9cf03b5d03cf1", size = 133386, upload-time = "2026-02-02T15:38:37.704Z" }, + { url = "https://files.pythonhosted.org/packages/b9/0e/45e1dcf10e17d0924b7c9162f87ec7b4ca79e28a0548acf6a71788d3e108/orjson-3.11.7-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:bda117c4148e81f746655d5a3239ae9bd00cb7bc3ca178b5fc5a5997e9744183", size = 138295, upload-time = "2026-02-02T15:38:39.096Z" }, + { url = "https://files.pythonhosted.org/packages/63/d7/4d2e8b03561257af0450f2845b91fbd111d7e526ccdf737267108075e0ba/orjson-3.11.7-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:23d6c20517a97a9daf1d48b580fcdc6f0516c6f4b5038823426033690b4d2650", size = 408720, upload-time = "2026-02-02T15:38:40.634Z" }, + { url = "https://files.pythonhosted.org/packages/78/cf/d45343518282108b29c12a65892445fc51f9319dc3c552ceb51bb5905ed2/orjson-3.11.7-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:8ff206156006da5b847c9304b6308a01e8cdbc8cce824e2779a5ba71c3def141", size = 144152, upload-time = "2026-02-02T15:38:42.262Z" }, + { url = "https://files.pythonhosted.org/packages/a9/3a/d6001f51a7275aacd342e77b735c71fa04125a3f93c36fee4526bc8c654e/orjson-3.11.7-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:962d046ee1765f74a1da723f4b33e3b228fe3a48bd307acce5021dfefe0e29b2", size = 134814, upload-time = "2026-02-02T15:38:43.627Z" }, + { url = "https://files.pythonhosted.org/packages/1d/d3/f19b47ce16820cc2c480f7f1723e17f6d411b3a295c60c8ad3aa9ff1c96a/orjson-3.11.7-cp314-cp314-win32.whl", hash = "sha256:89e13dd3f89f1c38a9c9eba5fbf7cdc2d1feca82f5f290864b4b7a6aac704576", size = 127997, upload-time = "2026-02-02T15:38:45.06Z" }, + { url = "https://files.pythonhosted.org/packages/12/df/172771902943af54bf661a8d102bdf2e7f932127968080632bda6054b62c/orjson-3.11.7-cp314-cp314-win_amd64.whl", hash = "sha256:845c3e0d8ded9c9271cd79596b9b552448b885b97110f628fb687aee2eed11c1", size = 124985, upload-time = "2026-02-02T15:38:46.388Z" }, + { url = "https://files.pythonhosted.org/packages/6f/1c/f2a8d8a1b17514660a614ce5f7aac74b934e69f5abc2700cc7ced882a009/orjson-3.11.7-cp314-cp314-win_arm64.whl", hash = "sha256:4a2e9c5be347b937a2e0203866f12bba36082e89b402ddb9e927d5822e43088d", size = 126038, upload-time = "2026-02-02T15:38:47.703Z" }, ] [[package]] @@ -2323,15 +2391,15 @@ wheels = [ [[package]] name = "python-discovery" -version = "1.1.3" +version = "1.2.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "filelock" }, { name = "platformdirs" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d7/7e/9f3b0dd3a074a6c3e1e79f35e465b1f2ee4b262d619de00cfce523cc9b24/python_discovery-1.1.3.tar.gz", hash = "sha256:7acca36e818cd88e9b2ba03e045ad7e93e1713e29c6bbfba5d90202310b7baa5", size = 56945, upload-time = "2026-03-10T15:08:15.038Z" } +sdist = { url = "https://files.pythonhosted.org/packages/9c/90/bcce6b46823c9bec1757c964dc37ed332579be512e17a30e9698095dcae4/python_discovery-1.2.0.tar.gz", hash = "sha256:7d33e350704818b09e3da2bd419d37e21e7c30db6e0977bb438916e06b41b5b1", size = 58055, upload-time = "2026-03-19T01:43:08.248Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e7/80/73211fc5bfbfc562369b4aa61dc1e4bf07dc7b34df7b317e4539316b809c/python_discovery-1.1.3-py3-none-any.whl", hash = "sha256:90e795f0121bc84572e737c9aa9966311b9fde44ffb88a5953b3ec9b31c6945e", size = 31485, upload-time = "2026-03-10T15:08:13.06Z" }, + { url = "https://files.pythonhosted.org/packages/c2/3c/2005227cb951df502412de2fa781f800663cccbef8d90ec6f1b371ac2c0d/python_discovery-1.2.0-py3-none-any.whl", hash = "sha256:1e108f1bbe2ed0ef089823d28805d5ad32be8e734b86a5f212bf89b71c266e4a", size = 31524, upload-time = "2026-03-19T01:43:07.045Z" }, ] [[package]] @@ -2496,7 +2564,7 @@ dependencies = [ [[package]] name = "reflex-enterprise" -version = "0.6.3" +version = "0.6.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "asgiproxy" }, @@ -2505,9 +2573,9 @@ dependencies = [ { name = "psutil" }, { name = "reflex" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/7e/f4/891da506c903a82cffb35d649595d02f8266436df26edea8cc7c02a3541d/reflex_enterprise-0.6.3.tar.gz", hash = "sha256:127c5a34294b0c4ea0b37bd4a89e7f3c70084fe1e24d448788417bc4def1f765", size = 390618, upload-time = "2026-03-16T17:17:25.541Z" } +sdist = { url = "https://files.pythonhosted.org/packages/2f/51/ef82746c384687d4c71f44be2294c40e4da0be35d546b45d5b14bd03203e/reflex_enterprise-0.6.4.tar.gz", hash = "sha256:1e84f97d9b4cf9a275304f164898cd9208c34176e5771b1765ea842c42611afb", size = 390702, upload-time = "2026-03-18T17:41:26.158Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d3/fd/27a189987e68cf89c77fa2acdab98d7a1e1ea5cea08139c0c4a30ea947f9/reflex_enterprise-0.6.3-py3-none-any.whl", hash = "sha256:a5a5a1d9e674b601b764122df8c09ed7907838864141b7dbf544d323903cb662", size = 216182, upload-time = "2026-03-16T17:17:26.897Z" }, + { url = "https://files.pythonhosted.org/packages/5b/74/79533d03f79ca498893f9f98faa12084423a4c1af69a715d62ed394f95c8/reflex_enterprise-0.6.4-py3-none-any.whl", hash = "sha256:ac9dab9031872c4fe391d2e881a9306ce983c2cb7532a10711ac3c0fc0add9c2", size = 216259, upload-time = "2026-03-18T17:41:27.584Z" }, ] [[package]] @@ -2553,7 +2621,7 @@ wheels = [ [[package]] name = "reflex-ui" version = "0.0.1" -source = { git = "https://github.com/reflex-dev/reflex-ui?rev=main#9bbec190f524d5db35921bc0798e2f50a20a5da1" } +source = { git = "https://github.com/reflex-dev/reflex-ui?rev=main#d03dadce92bb305c41c8431b793dab81cda90ee5" } dependencies = [ { name = "reflex" }, ] @@ -2569,6 +2637,7 @@ dependencies = [ { name = "googletrans-py" }, { name = "mistletoe" }, { name = "openai" }, + { name = "orjson" }, { name = "pandas" }, { name = "plotly-express" }, { name = "psycopg", extra = ["binary"] }, @@ -2580,7 +2649,6 @@ dependencies = [ { name = "reflex-image-zoom" }, { name = "reflex-pyplot" }, { name = "reflex-ui" }, - { name = "replicate" }, { name = "requests" }, { name = "ruff-format" }, { name = "typesense" }, @@ -2604,6 +2672,7 @@ requires-dist = [ { name = "googletrans-py" }, { name = "mistletoe" }, { name = "openai" }, + { name = "orjson" }, { name = "pandas" }, { name = "plotly-express" }, { name = "psycopg", extras = ["binary"] }, @@ -2615,7 +2684,6 @@ requires-dist = [ { name = "reflex-image-zoom" }, { name = "reflex-pyplot" }, { name = "reflex-ui", git = "https://github.com/reflex-dev/reflex-ui?rev=main" }, - { name = "replicate" }, { name = "requests" }, { name = "ruff-format" }, { name = "typesense" }, @@ -2631,21 +2699,6 @@ dev = [ { name = "uvicorn" }, ] -[[package]] -name = "replicate" -version = "1.0.7" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "httpx" }, - { name = "packaging" }, - { name = "pydantic" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/4b/fd/caf6c59a6b8007366bd52ab5a320bf8d828f3860a60039309cfc0e375ec9/replicate-1.0.7.tar.gz", hash = "sha256:d88cb2c37ba39fb370c87fc3291601c67aae64bb918a20a85b5ce399c23ee84c", size = 62226, upload-time = "2025-05-27T11:29:08.111Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c2/5a/b3aa02a11a33de08e7771579154af3193decfb9d923b30b14c17b4e8bbce/replicate-1.0.7-py3-none-any.whl", hash = "sha256:667c50a9eb83be17de6278ff89483102b3b50f49a2c7fbcaa2e2b14df13816f9", size = 48626, upload-time = "2025-05-27T11:29:06.801Z" }, -] - [[package]] name = "requests" version = "2.32.5" @@ -2676,63 +2729,65 @@ wheels = [ [[package]] name = "ruff" -version = "0.15.6" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/51/df/f8629c19c5318601d3121e230f74cbee7a3732339c52b21daa2b82ef9c7d/ruff-0.15.6.tar.gz", hash = "sha256:8394c7bb153a4e3811a4ecdacd4a8e6a4fa8097028119160dffecdcdf9b56ae4", size = 4597916, upload-time = "2026-03-12T23:05:47.51Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9e/2f/4e03a7e5ce99b517e98d3b4951f411de2b0fa8348d39cf446671adcce9a2/ruff-0.15.6-py3-none-linux_armv6l.whl", hash = "sha256:7c98c3b16407b2cf3d0f2b80c80187384bc92c6774d85fefa913ecd941256fff", size = 10508953, upload-time = "2026-03-12T23:05:17.246Z" }, - { url = "https://files.pythonhosted.org/packages/70/60/55bcdc3e9f80bcf39edf0cd272da6fa511a3d94d5a0dd9e0adf76ceebdb4/ruff-0.15.6-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ee7dcfaad8b282a284df4aa6ddc2741b3f4a18b0555d626805555a820ea181c3", size = 10942257, upload-time = "2026-03-12T23:05:23.076Z" }, - { url = "https://files.pythonhosted.org/packages/e7/f9/005c29bd1726c0f492bfa215e95154cf480574140cb5f867c797c18c790b/ruff-0.15.6-py3-none-macosx_11_0_arm64.whl", hash = "sha256:3bd9967851a25f038fc8b9ae88a7fbd1b609f30349231dffaa37b6804923c4bb", size = 10322683, upload-time = "2026-03-12T23:05:33.738Z" }, - { url = "https://files.pythonhosted.org/packages/5f/74/2f861f5fd7cbb2146bddb5501450300ce41562da36d21868c69b7a828169/ruff-0.15.6-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:13f4594b04e42cd24a41da653886b04d2ff87adbf57497ed4f728b0e8a4866f8", size = 10660986, upload-time = "2026-03-12T23:05:53.245Z" }, - { url = "https://files.pythonhosted.org/packages/c1/a1/309f2364a424eccb763cdafc49df843c282609f47fe53aa83f38272389e0/ruff-0.15.6-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e2ed8aea2f3fe57886d3f00ea5b8aae5bf68d5e195f487f037a955ff9fbaac9e", size = 10332177, upload-time = "2026-03-12T23:05:56.145Z" }, - { url = "https://files.pythonhosted.org/packages/30/41/7ebf1d32658b4bab20f8ac80972fb19cd4e2c6b78552be263a680edc55ac/ruff-0.15.6-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:70789d3e7830b848b548aae96766431c0dc01a6c78c13381f423bf7076c66d15", size = 11170783, upload-time = "2026-03-12T23:06:01.742Z" }, - { url = "https://files.pythonhosted.org/packages/76/be/6d488f6adca047df82cd62c304638bcb00821c36bd4881cfca221561fdfc/ruff-0.15.6-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:542aaf1de3154cea088ced5a819ce872611256ffe2498e750bbae5247a8114e9", size = 12044201, upload-time = "2026-03-12T23:05:28.697Z" }, - { url = "https://files.pythonhosted.org/packages/71/68/e6f125df4af7e6d0b498f8d373274794bc5156b324e8ab4bf5c1b4fc0ec7/ruff-0.15.6-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c22e6f02c16cfac3888aa636e9eba857254d15bbacc9906c9689fdecb1953ab", size = 11421561, upload-time = "2026-03-12T23:05:31.236Z" }, - { url = "https://files.pythonhosted.org/packages/f1/9f/f85ef5fd01a52e0b472b26dc1b4bd228b8f6f0435975442ffa4741278703/ruff-0.15.6-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98893c4c0aadc8e448cfa315bd0cc343a5323d740fe5f28ef8a3f9e21b381f7e", size = 11310928, upload-time = "2026-03-12T23:05:45.288Z" }, - { url = "https://files.pythonhosted.org/packages/8c/26/b75f8c421f5654304b89471ed384ae8c7f42b4dff58fa6ce1626d7f2b59a/ruff-0.15.6-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:70d263770d234912374493e8cc1e7385c5d49376e41dfa51c5c3453169dc581c", size = 11235186, upload-time = "2026-03-12T23:05:50.677Z" }, - { url = "https://files.pythonhosted.org/packages/fc/d4/d5a6d065962ff7a68a86c9b4f5500f7d101a0792078de636526c0edd40da/ruff-0.15.6-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:55a1ad63c5a6e54b1f21b7514dfadc0c7fb40093fa22e95143cf3f64ebdcd512", size = 10635231, upload-time = "2026-03-12T23:05:37.044Z" }, - { url = "https://files.pythonhosted.org/packages/d6/56/7c3acf3d50910375349016cf33de24be021532042afbed87942858992491/ruff-0.15.6-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:8dc473ba093c5ec238bb1e7429ee676dca24643c471e11fbaa8a857925b061c0", size = 10340357, upload-time = "2026-03-12T23:06:04.748Z" }, - { url = "https://files.pythonhosted.org/packages/06/54/6faa39e9c1033ff6a3b6e76b5df536931cd30caf64988e112bbf91ef5ce5/ruff-0.15.6-py3-none-musllinux_1_2_i686.whl", hash = "sha256:85b042377c2a5561131767974617006f99f7e13c63c111b998f29fc1e58a4cfb", size = 10860583, upload-time = "2026-03-12T23:05:58.978Z" }, - { url = "https://files.pythonhosted.org/packages/cb/1e/509a201b843b4dfb0b32acdedf68d951d3377988cae43949ba4c4133a96a/ruff-0.15.6-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:cef49e30bc5a86a6a92098a7fbf6e467a234d90b63305d6f3ec01225a9d092e0", size = 11410976, upload-time = "2026-03-12T23:05:39.955Z" }, - { url = "https://files.pythonhosted.org/packages/6c/25/3fc9114abf979a41673ce877c08016f8e660ad6cf508c3957f537d2e9fa9/ruff-0.15.6-py3-none-win32.whl", hash = "sha256:bbf67d39832404812a2d23020dda68fee7f18ce15654e96fb1d3ad21a5fe436c", size = 10616872, upload-time = "2026-03-12T23:05:42.451Z" }, - { url = "https://files.pythonhosted.org/packages/89/7a/09ece68445ceac348df06e08bf75db72d0e8427765b96c9c0ffabc1be1d9/ruff-0.15.6-py3-none-win_amd64.whl", hash = "sha256:aee25bc84c2f1007ecb5037dff75cef00414fdf17c23f07dc13e577883dca406", size = 11787271, upload-time = "2026-03-12T23:05:20.168Z" }, - { url = "https://files.pythonhosted.org/packages/7f/d0/578c47dd68152ddddddf31cd7fc67dc30b7cdf639a86275fda821b0d9d98/ruff-0.15.6-py3-none-win_arm64.whl", hash = "sha256:c34de3dd0b0ba203be50ae70f5910b17188556630e2178fd7d79fc030eb0d837", size = 11060497, upload-time = "2026-03-12T23:05:25.968Z" }, +version = "0.15.7" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a1/22/9e4f66ee588588dc6c9af6a994e12d26e19efbe874d1a909d09a6dac7a59/ruff-0.15.7.tar.gz", hash = "sha256:04f1ae61fc20fe0b148617c324d9d009b5f63412c0b16474f3d5f1a1a665f7ac", size = 4601277, upload-time = "2026-03-19T16:26:22.605Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/41/2f/0b08ced94412af091807b6119ca03755d651d3d93a242682bf020189db94/ruff-0.15.7-py3-none-linux_armv6l.whl", hash = "sha256:a81cc5b6910fb7dfc7c32d20652e50fa05963f6e13ead3c5915c41ac5d16668e", size = 10489037, upload-time = "2026-03-19T16:26:32.47Z" }, + { url = "https://files.pythonhosted.org/packages/91/4a/82e0fa632e5c8b1eba5ee86ecd929e8ff327bbdbfb3c6ac5d81631bef605/ruff-0.15.7-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:722d165bd52403f3bdabc0ce9e41fc47070ac56d7a91b4e0d097b516a53a3477", size = 10955433, upload-time = "2026-03-19T16:27:00.205Z" }, + { url = "https://files.pythonhosted.org/packages/ab/10/12586735d0ff42526ad78c049bf51d7428618c8b5c467e72508c694119df/ruff-0.15.7-py3-none-macosx_11_0_arm64.whl", hash = "sha256:7fbc2448094262552146cbe1b9643a92f66559d3761f1ad0656d4991491af49e", size = 10269302, upload-time = "2026-03-19T16:26:26.183Z" }, + { url = "https://files.pythonhosted.org/packages/eb/5d/32b5c44ccf149a26623671df49cbfbd0a0ae511ff3df9d9d2426966a8d57/ruff-0.15.7-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b39329b60eba44156d138275323cc726bbfbddcec3063da57caa8a8b1d50adf", size = 10607625, upload-time = "2026-03-19T16:27:03.263Z" }, + { url = "https://files.pythonhosted.org/packages/5d/f1/f0001cabe86173aaacb6eb9bb734aa0605f9a6aa6fa7d43cb49cbc4af9c9/ruff-0.15.7-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:87768c151808505f2bfc93ae44e5f9e7c8518943e5074f76ac21558ef5627c85", size = 10324743, upload-time = "2026-03-19T16:27:09.791Z" }, + { url = "https://files.pythonhosted.org/packages/7a/87/b8a8f3d56b8d848008559e7c9d8bf367934d5367f6d932ba779456e2f73b/ruff-0.15.7-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fb0511670002c6c529ec66c0e30641c976c8963de26a113f3a30456b702468b0", size = 11138536, upload-time = "2026-03-19T16:27:06.101Z" }, + { url = "https://files.pythonhosted.org/packages/e4/f2/4fd0d05aab0c5934b2e1464784f85ba2eab9d54bffc53fb5430d1ed8b829/ruff-0.15.7-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e0d19644f801849229db8345180a71bee5407b429dd217f853ec515e968a6912", size = 11994292, upload-time = "2026-03-19T16:26:48.718Z" }, + { url = "https://files.pythonhosted.org/packages/64/22/fc4483871e767e5e95d1622ad83dad5ebb830f762ed0420fde7dfa9d9b08/ruff-0.15.7-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4806d8e09ef5e84eb19ba833d0442f7e300b23fe3f0981cae159a248a10f0036", size = 11398981, upload-time = "2026-03-19T16:26:54.513Z" }, + { url = "https://files.pythonhosted.org/packages/b0/99/66f0343176d5eab02c3f7fcd2de7a8e0dd7a41f0d982bee56cd1c24db62b/ruff-0.15.7-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dce0896488562f09a27b9c91b1f58a097457143931f3c4d519690dea54e624c5", size = 11242422, upload-time = "2026-03-19T16:26:29.277Z" }, + { url = "https://files.pythonhosted.org/packages/5d/3a/a7060f145bfdcce4c987ea27788b30c60e2c81d6e9a65157ca8afe646328/ruff-0.15.7-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:1852ce241d2bc89e5dc823e03cff4ce73d816b5c6cdadd27dbfe7b03217d2a12", size = 11232158, upload-time = "2026-03-19T16:26:42.321Z" }, + { url = "https://files.pythonhosted.org/packages/a7/53/90fbb9e08b29c048c403558d3cdd0adf2668b02ce9d50602452e187cd4af/ruff-0.15.7-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:5f3e4b221fb4bd293f79912fc5e93a9063ebd6d0dcbd528f91b89172a9b8436c", size = 10577861, upload-time = "2026-03-19T16:26:57.459Z" }, + { url = "https://files.pythonhosted.org/packages/2f/aa/5f486226538fe4d0f0439e2da1716e1acf895e2a232b26f2459c55f8ddad/ruff-0.15.7-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:b15e48602c9c1d9bdc504b472e90b90c97dc7d46c7028011ae67f3861ceba7b4", size = 10327310, upload-time = "2026-03-19T16:26:35.909Z" }, + { url = "https://files.pythonhosted.org/packages/99/9e/271afdffb81fe7bfc8c43ba079e9d96238f674380099457a74ccb3863857/ruff-0.15.7-py3-none-musllinux_1_2_i686.whl", hash = "sha256:1b4705e0e85cedc74b0a23cf6a179dbb3df184cb227761979cc76c0440b5ab0d", size = 10840752, upload-time = "2026-03-19T16:26:45.723Z" }, + { url = "https://files.pythonhosted.org/packages/bf/29/a4ae78394f76c7759953c47884eb44de271b03a66634148d9f7d11e721bd/ruff-0.15.7-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:112c1fa316a558bb34319282c1200a8bf0495f1b735aeb78bfcb2991e6087580", size = 11336961, upload-time = "2026-03-19T16:26:39.076Z" }, + { url = "https://files.pythonhosted.org/packages/26/6b/8786ba5736562220d588a2f6653e6c17e90c59ced34a2d7b512ef8956103/ruff-0.15.7-py3-none-win32.whl", hash = "sha256:6d39e2d3505b082323352f733599f28169d12e891f7dd407f2d4f54b4c2886de", size = 10582538, upload-time = "2026-03-19T16:26:15.992Z" }, + { url = "https://files.pythonhosted.org/packages/2b/e9/346d4d3fffc6871125e877dae8d9a1966b254fbd92a50f8561078b88b099/ruff-0.15.7-py3-none-win_amd64.whl", hash = "sha256:4d53d712ddebcd7dace1bc395367aec12c057aacfe9adbb6d832302575f4d3a1", size = 11755839, upload-time = "2026-03-19T16:26:19.897Z" }, + { url = "https://files.pythonhosted.org/packages/8f/e8/726643a3ea68c727da31570bde48c7a10f1aa60eddd628d94078fec586ff/ruff-0.15.7-py3-none-win_arm64.whl", hash = "sha256:18e8d73f1c3fdf27931497972250340f92e8c861722161a9caeb89a58ead6ed2", size = 11023304, upload-time = "2026-03-19T16:26:51.669Z" }, ] [[package]] name = "ruff-format" -version = "0.4.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e6/ae/5767b436b41af8add7432286fce65a489a4db88ed090fb66275fa0076cd3/ruff_format-0.4.1.tar.gz", hash = "sha256:2aff271154b088ee131cef63a92afbc4cdc3905acf03d279c4a8aa3f6b3fb564", size = 15622, upload-time = "2025-10-28T18:31:39.817Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2e/00/2d7778a97bcae6a3e1ddbc740936b0fcc7e7abe2e0ee054b18b4100bed5c/ruff_format-0.4.1-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:ced72ef509b427483b27c7d85411fe9e11cba38cfd20f899579a22f7582fa598", size = 2148646, upload-time = "2025-10-28T18:31:25.359Z" }, - { url = "https://files.pythonhosted.org/packages/be/da/1c136748eeb09609c06859fdfec93e3f35f928ca2be7ca34973df172bb39/ruff_format-0.4.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:af3df907875a5ac8e156a9b63163fa4385ba629d743970b20a86a1f6eaaf8f20", size = 2087325, upload-time = "2025-10-28T18:31:22.905Z" }, - { url = "https://files.pythonhosted.org/packages/54/b2/bba49938eeeb6b57a26cd86923c82d7fa52f0ee80cb79aad0e3cc75ca815/ruff_format-0.4.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:226155424d5454998697593d70518a77a6fb85ffb78f334e9ec3e651977289da", size = 2275529, upload-time = "2025-10-28T18:31:05.102Z" }, - { url = "https://files.pythonhosted.org/packages/18/43/ea43a81b45a62c0b4475d85062c829de24b8465255f1af01ba8819db5dac/ruff_format-0.4.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b363a61c116e5d74f4909b95708049a5f1a577a8fb0e75ac4d1b2bd02eac7440", size = 2221105, upload-time = "2025-10-28T18:31:08.147Z" }, - { url = "https://files.pythonhosted.org/packages/c4/5f/7127239e4aeec9afe61bb37c41296c8cd858b5ad4b7f46eeb8ba418b9f1f/ruff_format-0.4.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a990c9439bd658d481641ba87051471f9f205a33d32dbfa2c4aa1b3448eea3bb", size = 3136332, upload-time = "2025-10-28T18:31:11.572Z" }, - { url = "https://files.pythonhosted.org/packages/cc/b9/b59a438d9197e73347f4b6fad498e36f41fd3c56984614ed016f7b53fb2e/ruff_format-0.4.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:014714c227f2d12eb988bc1913aa58f8113aa25b0056220eff77ed9e2a5c31bd", size = 2431819, upload-time = "2025-10-28T18:31:14.559Z" }, - { url = "https://files.pythonhosted.org/packages/6c/e9/1a365b24d5c5bf1838a267eda49a399dd491104740210d11db817ca8dbf7/ruff_format-0.4.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a6adf7223143f42c89cb05cb0dd3ff2fc578c57ea84de6e0809b9e3324ff926c", size = 2331924, upload-time = "2025-10-28T18:31:19.791Z" }, - { url = "https://files.pythonhosted.org/packages/2e/79/79b8f418bce106a45a9c56c10722266782f3d987351aa6e88f385c27f99f/ruff_format-0.4.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:56e17ee28d47e4572f495221e96cc6fb87102aaed8f48110816e0c02768f2a6e", size = 2402844, upload-time = "2025-10-28T18:31:17.066Z" }, - { url = "https://files.pythonhosted.org/packages/67/e7/fa9e7d5b48a5bf0c285428f9861e389dbd0b6dae0040a4a16db02416090b/ruff_format-0.4.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:67f4402da91604d73dac8ce35a56419cf3ecd874ff8d26cd82122220a5fdca25", size = 2455140, upload-time = "2025-10-28T18:31:28.368Z" }, - { url = "https://files.pythonhosted.org/packages/49/0c/384d7983e6b33076e8d96aa96f669120932816d8e837f70495299d71459a/ruff_format-0.4.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:c3300140c01622a3f43e968dd226a42d19b1e6d5d9ce45c8c595e8f5918aa8cf", size = 2488239, upload-time = "2025-10-28T18:31:31.463Z" }, - { url = "https://files.pythonhosted.org/packages/ac/6d/df79fca14652a70b287458626bbadb26978086395726c0010f0438e114e6/ruff_format-0.4.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:8bc80217523edec20d76f03fe65872278a347f09ccfe4993641d0687782202cc", size = 2493273, upload-time = "2025-10-28T18:31:34.374Z" }, - { url = "https://files.pythonhosted.org/packages/33/50/f46613603c24a33377e129b75499585f88e89cdeae0bb3e92be6c717f02e/ruff_format-0.4.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:d127ceb09f8938ceb91a9750dce59ac03e81e6c148a18d4baa2ecccb3df68bef", size = 2505607, upload-time = "2025-10-28T18:31:37.089Z" }, - { url = "https://files.pythonhosted.org/packages/3b/c0/6ebccef59083405b77a5c95779d1acece038f0b5ed223b19cc1704d2755d/ruff_format-0.4.1-cp313-cp313t-win32.whl", hash = "sha256:75c7f4cb08466dbd1d042c0c6013d25d4b61780f08aa05a8384bb553a73d88d7", size = 1818617, upload-time = "2025-10-28T18:31:42.864Z" }, - { url = "https://files.pythonhosted.org/packages/7b/ee/17aeeffa8dd4241360a8c6f9937e99cbbfb9d52dc033d382c9d4a87fec0e/ruff_format-0.4.1-cp313-cp313t-win_amd64.whl", hash = "sha256:0f5dccef951c5e161087c930ccbd8969712acc6081dce520fe31555025d5602f", size = 1923603, upload-time = "2025-10-28T18:31:40.526Z" }, - { url = "https://files.pythonhosted.org/packages/a9/0f/47755cda55e7d55211e43d5de61d80bd75d1c0f63d3df996d53d5cfbe1c5/ruff_format-0.4.1-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:60c5aca1dc8b0fa5133bd7983035999cce03daf97da24668945d69114c630dc8", size = 2153211, upload-time = "2025-10-28T18:31:26.698Z" }, - { url = "https://files.pythonhosted.org/packages/10/03/daeff0742bc47c2fc56d250049bdf6b81074d23dcab3b9486c9c33857cb1/ruff_format-0.4.1-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:f1eb8a30f2c03d27764a2985b56b2053d2f2e74a65cc98550edbc71aa4bfbb3c", size = 2090816, upload-time = "2025-10-28T18:31:24.175Z" }, - { url = "https://files.pythonhosted.org/packages/81/f0/64525240a46f1bdc9e7cf7c53342ab271c2ab22cd10f4c016153b6582ecb/ruff_format-0.4.1-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ae67a0d1d9b6826deefbbf3c326bcf9a4d4e6cd46536f8f7d33d2cd06bc79b97", size = 2280121, upload-time = "2025-10-28T18:31:06.578Z" }, - { url = "https://files.pythonhosted.org/packages/82/56/192aaa9e3ea2003f1da44cf3d766b93325dfae41ffbdc0133506456af06c/ruff_format-0.4.1-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7edcbaed5c66e46bf1c57efa93151624cfe6eb7f2af0864fe6194da4dad01524", size = 2226372, upload-time = "2025-10-28T18:31:09.988Z" }, - { url = "https://files.pythonhosted.org/packages/3c/a8/8759d7525b394b78dfa8484eba64db8373293f655e42a2ea48bf18a4028c/ruff_format-0.4.1-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0151c72b3f4a8eb2dbbcee360e68581676e7e490cd3cc4ba098fb96a831659db", size = 3139010, upload-time = "2025-10-28T18:31:12.958Z" }, - { url = "https://files.pythonhosted.org/packages/5a/70/5c52d0dd2d8d5287b4e27724f022f7229d4c45fe0f75f53d659f67d408f6/ruff_format-0.4.1-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:00e0a83bd9a7acc8d50a63d8057441085d7498e94b130377082bea4cc57fdc62", size = 2437646, upload-time = "2025-10-28T18:31:15.862Z" }, - { url = "https://files.pythonhosted.org/packages/66/99/b89a34911e7287505e13efa487f2488e9a537d2429b37f465c94ea6612bf/ruff_format-0.4.1-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9086e8efea0925699b1c2a649922b936356d88f64c36b859aac2ed6033991042", size = 2338267, upload-time = "2025-10-28T18:31:21.419Z" }, - { url = "https://files.pythonhosted.org/packages/ff/02/92178e6b14b93f7dfed57c6c5f249ad7b858218aaf55f303f932d04d6459/ruff_format-0.4.1-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:be26579b974401f8e6cadd4837bdb4033696740758973c43dc470bd8404abf0a", size = 2408411, upload-time = "2025-10-28T18:31:18.593Z" }, - { url = "https://files.pythonhosted.org/packages/4a/d8/61f41a959595adfb00f60c9a8fcbb0b5d32c69d32cafbdf807d38cd543a9/ruff_format-0.4.1-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:95f3c7dee3067616aaeb1d3e08968ea1d71e030c1c9704524ad91149df1cbda2", size = 2460360, upload-time = "2025-10-28T18:31:30.19Z" }, - { url = "https://files.pythonhosted.org/packages/f3/1e/d32bea94d19e1a5df9cbb581ffe46b25d72f2f13dc380ebc301366dd0791/ruff_format-0.4.1-cp38-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:c00576aac8e66548d778116be4e8d83abf994303c9426d2d9fa3ee47e952ebeb", size = 2492780, upload-time = "2025-10-28T18:31:32.804Z" }, - { url = "https://files.pythonhosted.org/packages/a8/71/b6d9b84cbd716a2c968c9cfe365070d2f5411e448287ed925d9c8786bade/ruff_format-0.4.1-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:15ad2649a213a78f907893a42712381e7a0f66107e5d5f48e9891657a5147852", size = 2498606, upload-time = "2025-10-28T18:31:35.818Z" }, - { url = "https://files.pythonhosted.org/packages/ec/2e/3f40d244fdd5bdbe993f55d46e2285900d4b03f0a50fdcd8280ce8635209/ruff_format-0.4.1-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a322cd9062664d0461efc804418b349c30a68e1e58c8794b1e9b28aa57ee02a2", size = 2509832, upload-time = "2025-10-28T18:31:38.757Z" }, - { url = "https://files.pythonhosted.org/packages/59/86/3253d7f4354a104e1e79110d740f4293042fd399981c59653a8353dc450a/ruff_format-0.4.1-cp38-abi3-win32.whl", hash = "sha256:66524a2088eb0f2bb95f297ad8bda2bec0143e9d690bbce76a2b326b4e668968", size = 1823361, upload-time = "2025-10-28T18:31:44.07Z" }, - { url = "https://files.pythonhosted.org/packages/49/2d/7f20db74ff5156900852077026771bd20f3ca1ba2a4987d98e3385c13ad8/ruff_format-0.4.1-cp38-abi3-win_amd64.whl", hash = "sha256:3f5360ebfe9465c4bb803dd69c6cdc38610860ee9087b40ae038a595c4779653", size = 1927799, upload-time = "2025-10-28T18:31:41.716Z" }, +version = "0.5.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e2/a1/772ecfd1973e9536fc6731f03faba3e90fdcf065a39db4d9b0a5f3ad533b/ruff_format-0.5.1.tar.gz", hash = "sha256:e302b58e9805f9fcdb74230a5502e869b02f6472bb911c51dd0c9d87e37f76b3", size = 16102, upload-time = "2026-03-20T22:24:39.874Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/74/35/15d0cfa3288cce2874bd3b33f1a64c27625f0d9ca55927143f5612c3c5ce/ruff_format-0.5.1-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:04cb4ed8b6f302ac9f42abc991634dc3e00d0aee6d65bfb09bfacfd532449cfe", size = 2216286, upload-time = "2026-03-20T22:24:31.552Z" }, + { url = "https://files.pythonhosted.org/packages/64/be/848e3724014fc3bac275d796d37b83de5814941a755d9d155f82ae26bc27/ruff_format-0.5.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ecf4c050f95cc972750d53a32adbf2d1de338abf51def126c94d64865449d998", size = 2172236, upload-time = "2026-03-20T22:24:36.963Z" }, + { url = "https://files.pythonhosted.org/packages/0b/d4/3d781dd5a23584c59cd7b7be40b33a0f2fdcb555e7f0806980b8df7a4bef/ruff_format-0.5.1-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:74fd36e7070944e3e987a46f4f0a83583ecbc79ccfa99a54146cd4371e753a16", size = 2361217, upload-time = "2026-03-20T22:24:48.963Z" }, + { url = "https://files.pythonhosted.org/packages/b9/34/508fada0ada0905569fdd3e238727f53214f507bd79e579bf8871cd9188c/ruff_format-0.5.1-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f942343d8979590c72e48b358c9c262296d3d9d135aa71bb152c5086d33e87b1", size = 2282607, upload-time = "2026-03-20T22:24:42.057Z" }, + { url = "https://files.pythonhosted.org/packages/0f/77/67c0e2dfa50e2fcd50f3717c770dd24bacd70afd864384e24745ccf471af/ruff_format-0.5.1-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5318e8d5bd2cb98178e8df9b579e4e16026317adce968ec5631fe6ad485ba945", size = 3258138, upload-time = "2026-03-20T22:25:03.795Z" }, + { url = "https://files.pythonhosted.org/packages/76/f8/f62431b29bb4e3982d35ed352bf97669ee53b86f7153624849f38a214016/ruff_format-0.5.1-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c03b29292b885b84f8e14e6e5b588b761b5402ffc5fd702cd2c64904fe1be6e3", size = 2406034, upload-time = "2026-03-20T22:24:50.351Z" }, + { url = "https://files.pythonhosted.org/packages/60/0c/73312194a12513c00867ea167b14acfffeeca8719ec29461c6183bc97245/ruff_format-0.5.1-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb4e7981e33531212452a26c6abbefdb980d9ef00b68e607662c1fb61a7f5bfc", size = 2418286, upload-time = "2026-03-20T22:25:05.238Z" }, + { url = "https://files.pythonhosted.org/packages/f5/d3/5d3fcd14eb95bfb83a1855751fdb4ca34e8eada4df456ea52a4c40866548/ruff_format-0.5.1-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b9764cf852a140ebbca08387ed402d291cab08e2ddcd60ee8bc286c75be22bf5", size = 2477310, upload-time = "2026-03-20T22:24:44.682Z" }, + { url = "https://files.pythonhosted.org/packages/e9/65/756178b4b1da4d411ee7d7deb6fe64b6b2196bd4e5880efcceb21bcc7013/ruff_format-0.5.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7599bad5ce577a09ca96f9107fe2daf69710a5db898a7ddc3428d3e1ca1dcde7", size = 2535880, upload-time = "2026-03-20T22:24:38.201Z" }, + { url = "https://files.pythonhosted.org/packages/7d/ce/834a5982e91aae58a4944f529884784a8549e5a779ad834dcdcb2a48d91d/ruff_format-0.5.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:8774fb19225a32ca845a4cc90d4bc37d341abf991cf23c0d14c8fa9fc03a24f3", size = 2558439, upload-time = "2026-03-20T22:24:34.118Z" }, + { url = "https://files.pythonhosted.org/packages/b3/c1/54cb29eb1f5939699beb7cf43d2ab6c57ba055dd655a9eeca2e0ee7df91e/ruff_format-0.5.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:f41fe7bdd9e4e57d9bb6b161be48b0fcec582449ab5eba38b4edf2f7cdd2ae12", size = 2622222, upload-time = "2026-03-20T22:24:55.035Z" }, + { url = "https://files.pythonhosted.org/packages/a3/48/74fc8248843d6b0a87da0243527893d769940a6456a90d6fdc05b703ac40/ruff_format-0.5.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:f52489d0a13dd0f66c94cadad325b2ba59ad5739a040c306ae6fd393393389dc", size = 2621183, upload-time = "2026-03-20T22:24:56.464Z" }, + { url = "https://files.pythonhosted.org/packages/57/c1/9252cfb64488ec278015fd23e24dcdb64f4226fd1b5ea0be494b5de1a283/ruff_format-0.5.1-cp314-cp314t-win32.whl", hash = "sha256:84183e0762116c4198345d0dd08f3359c4f9d77d9cac80becb325c4984ab43c0", size = 1889723, upload-time = "2026-03-20T22:24:27.014Z" }, + { url = "https://files.pythonhosted.org/packages/d4/ff/a433f159ae2c273d4a2bcc9b43e443a879e968f3e1926276227c049f5d4e/ruff_format-0.5.1-cp314-cp314t-win_amd64.whl", hash = "sha256:6f9ce8cd4b0306bec96c09bc3a2059cf9950a6ee3a2ba287457f314392fe23db", size = 2030360, upload-time = "2026-03-20T22:24:47.577Z" }, + { url = "https://files.pythonhosted.org/packages/c9/f8/05d5a106a75af0def5efae8d85039836c3be10cbb54d5b763baa85aa7d60/ruff_format-0.5.1-cp314-cp314t-win_arm64.whl", hash = "sha256:605270334d9f52b529dd948c9616534d0c949d55e3fe68f61b23a706bd65aa72", size = 1959237, upload-time = "2026-03-20T22:25:01.108Z" }, + { url = "https://files.pythonhosted.org/packages/d2/c3/951cfe686b9c6e63365328ee893ad655b6f6bb5d71993d75d83a2a440ec7/ruff_format-0.5.1-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:6427f76974ff7963efe094745ad450925cfb9dd54d8b249dff37ac964d5fd8b0", size = 2219461, upload-time = "2026-03-20T22:24:58.082Z" }, + { url = "https://files.pythonhosted.org/packages/ca/e2/d0fa0d6ebcc30aae10ce206748666621c8debbdee1ebc820405d78202593/ruff_format-0.5.1-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:b823f61f9c274af6d49bc6c010bf3648f6d0b766557fbd5bab152020716646e0", size = 2174620, upload-time = "2026-03-20T22:24:40.671Z" }, + { url = "https://files.pythonhosted.org/packages/cf/18/77465ebe3a4f158906264a4580ca91aa19259b6bcca6f8855f8bf3cce34f/ruff_format-0.5.1-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:34f89deee48b35f7b73b524d19e058da4bd877ec37e46e97edebf0b1fb03f3f5", size = 2367247, upload-time = "2026-03-20T22:24:28.764Z" }, + { url = "https://files.pythonhosted.org/packages/ca/20/b9a4202e686b4be6f50c3937bcf4bdcbf865eb0cb4d76af50b6b6254492c/ruff_format-0.5.1-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4362fccc7c31bf874c570a58b13f7322d8365473ecd58e40bec493d78c2ff152", size = 2286075, upload-time = "2026-03-20T22:25:08.639Z" }, + { url = "https://files.pythonhosted.org/packages/fb/b5/3ed7e8980e386b44c0a540a812cc82081b4595f2b64f46518eebf3cbc413/ruff_format-0.5.1-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:60ae087d04b9361cb60fadf2e7f0ec9a5a009cf638629011ef69e7307bf5c0c1", size = 3261777, upload-time = "2026-03-20T22:24:35.589Z" }, + { url = "https://files.pythonhosted.org/packages/2c/00/c51ab08111e6ffc26d5f05e333c5c08a59a185b04c4c1fa92b848972832e/ruff_format-0.5.1-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:73975407cb5626bc158b7028f839e2a68fa00c4be05f991bdfac8f4c0bebcea4", size = 2410994, upload-time = "2026-03-20T22:24:32.788Z" }, + { url = "https://files.pythonhosted.org/packages/41/90/e5ce1ee4fbe21a5ea2b18021a201a4d87dfeda3f2b8b94e7925aadc0bc5d/ruff_format-0.5.1-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15422ec4776a7453c33111435a84f4c47020a83f9f58fa9558b82986ea57e365", size = 2422780, upload-time = "2026-03-20T22:24:43.325Z" }, + { url = "https://files.pythonhosted.org/packages/d5/b8/3890a941bec6ca97494cb1d8961397b8d6afb1bc18319c3d1c5b67ac41c9/ruff_format-0.5.1-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:70c06f820500f04cfccfe489772b3d1b797c416fc44e8b3cd3618f3bb7a8a1b4", size = 2483333, upload-time = "2026-03-20T22:24:46.339Z" }, + { url = "https://files.pythonhosted.org/packages/0e/d6/da1702a16015945afef1761e0f2da8e307fa8224b4ade5280f4d8e398db3/ruff_format-0.5.1-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:b8761e831404ab89d49af41817117cb933242d1d44c1169705f98fbb135e3fce", size = 2541749, upload-time = "2026-03-20T22:24:59.728Z" }, + { url = "https://files.pythonhosted.org/packages/53/30/aab3642e37c51f7fa5b7c87b20626f35295d0de6a67760606d90ef78a370/ruff_format-0.5.1-cp38-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:050cc37d7c95b1ee228b5c8f49517d2f99e7131fce0dbbcae8254e47e561cc68", size = 2563128, upload-time = "2026-03-20T22:24:51.975Z" }, + { url = "https://files.pythonhosted.org/packages/7d/e2/c28260cf14e178019dc258e26362cb159c415d5febf5905a7938982be116/ruff_format-0.5.1-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:b21d296a7f6968708862c00958d7c23bdd3407748b81748f2185a7f7632f4457", size = 2628397, upload-time = "2026-03-20T22:24:29.96Z" }, + { url = "https://files.pythonhosted.org/packages/4c/ab/f483ca39bb99b0391387336d94ef11c48de881aa70ab2a70b8a845368b67/ruff_format-0.5.1-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:2b40c76c4d4cd408d0b2e0978bc489960e03a4ef0da5dcd59f025f03931f018b", size = 2624062, upload-time = "2026-03-20T22:25:02.297Z" }, + { url = "https://files.pythonhosted.org/packages/df/b3/a5d04de80a5d1327acfcbb68279c3c18fa6e4d568c6fadba8fd94a333938/ruff_format-0.5.1-cp38-abi3-win32.whl", hash = "sha256:7e9c4c2a86b46074a63935427d813a85baf54b790c75bfbb7f72e1802d6df873", size = 1893014, upload-time = "2026-03-20T22:24:53.571Z" }, + { url = "https://files.pythonhosted.org/packages/30/96/ae74fb49ceb048c4dfe4ba6511ec6c675935ca6da8fd6112f07e2428f48b/ruff_format-0.5.1-cp38-abi3-win_amd64.whl", hash = "sha256:e346ced987bb586af3ec3cc99531e8d434a8094fbb41204fd03a8685c15b13b1", size = 2033389, upload-time = "2026-03-20T22:25:06.853Z" }, + { url = "https://files.pythonhosted.org/packages/67/7c/da06a044f9176471a1c5303c250481de6990ce4ca42c207548ae3c71ad8b/ruff_format-0.5.1-cp38-abi3-win_arm64.whl", hash = "sha256:22a042343bd1050573986a30286c7dfaf9479b78dbd731ab2fddedcbf050a412", size = 1963461, upload-time = "2026-03-20T22:25:09.834Z" }, ] [[package]] @@ -2904,15 +2959,15 @@ wheels = [ [[package]] name = "starlette" -version = "0.52.1" +version = "1.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, { name = "typing-extensions", marker = "python_full_version < '3.13'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c4/68/79977123bb7be889ad680d79a40f339082c1978b5cfcf62c2d8d196873ac/starlette-0.52.1.tar.gz", hash = "sha256:834edd1b0a23167694292e94f597773bc3f89f362be6effee198165a35d62933", size = 2653702, upload-time = "2026-01-18T13:34:11.062Z" } +sdist = { url = "https://files.pythonhosted.org/packages/81/69/17425771797c36cded50b7fe44e850315d039f28b15901ab44839e70b593/starlette-1.0.0.tar.gz", hash = "sha256:6a4beaf1f81bb472fd19ea9b918b50dc3a77a6f2e190a12954b25e6ed5eea149", size = 2655289, upload-time = "2026-03-22T18:29:46.779Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/81/0d/13d1d239a25cbfb19e740db83143e95c772a1fe10202dda4b76792b114dd/starlette-0.52.1-py3-none-any.whl", hash = "sha256:0029d43eb3d273bc4f83a08720b4912ea4b071087a3b48db01b7c839f7954d74", size = 74272, upload-time = "2026-01-18T13:34:09.188Z" }, + { url = "https://files.pythonhosted.org/packages/0b/c9/584bc9651441b4ba60cc4d557d8a547b5aff901af35bda3a4ee30c819b82/starlette-1.0.0-py3-none-any.whl", hash = "sha256:d3ec55e0bb321692d275455ddfd3df75fff145d009685eb40dc91fc66b03d38b", size = 72651, upload-time = "2026-03-22T18:29:45.111Z" }, ] [[package]]