diff --git a/.github/workflows/codespell.yml b/.github/workflows/codespell.yml
index 565c845c2..db7feb915 100644
--- a/.github/workflows/codespell.yml
+++ b/.github/workflows/codespell.yml
@@ -13,6 +13,6 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
- uses: actions/checkout@v3
+ uses: actions/checkout@v6
- name: Codespell
uses: codespell-project/actions-codespell@v2
diff --git a/.github/workflows/deploy-dev.yml b/.github/workflows/deploy-dev.yml
index 7a3f6650d..4ec24b6b9 100644
--- a/.github/workflows/deploy-dev.yml
+++ b/.github/workflows/deploy-dev.yml
@@ -24,12 +24,12 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
- uses: actions/checkout@v3
+ uses: actions/checkout@v6
with:
submodules: recursive
- name: Install the latest version of uv
- uses: astral-sh/setup-uv@v6
+ uses: astral-sh/setup-uv@v7
with:
python-version: "3.12"
activate-environment: true
diff --git a/.github/workflows/deploy-prd.yml b/.github/workflows/deploy-prd.yml
index 257af8eb9..33c960f76 100644
--- a/.github/workflows/deploy-prd.yml
+++ b/.github/workflows/deploy-prd.yml
@@ -22,12 +22,12 @@ jobs:
environment: production
steps:
- name: Checkout code
- uses: actions/checkout@v3
+ uses: actions/checkout@v6
with:
submodules: recursive
- name: Install the latest version of uv
- uses: astral-sh/setup-uv@v6
+ uses: astral-sh/setup-uv@v7
with:
python-version: "3.12"
activate-environment: true
diff --git a/.github/workflows/deploy-stg.yml b/.github/workflows/deploy-stg.yml
index 6e28cba24..98930283e 100644
--- a/.github/workflows/deploy-stg.yml
+++ b/.github/workflows/deploy-stg.yml
@@ -24,12 +24,12 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
- uses: actions/checkout@v3
+ uses: actions/checkout@v6
with:
submodules: recursive
- name: Install the latest version of uv
- uses: astral-sh/setup-uv@v6
+ uses: astral-sh/setup-uv@v7
with:
python-version: "3.12"
activate-environment: true
diff --git a/.github/workflows/integration_tests.yml b/.github/workflows/integration_tests.yml
index 626315fb7..1a03be2b5 100644
--- a/.github/workflows/integration_tests.yml
+++ b/.github/workflows/integration_tests.yml
@@ -25,12 +25,12 @@ jobs:
reflex-web:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v6
with:
submodules: recursive
- name: Install the latest version of uv
- uses: astral-sh/setup-uv@v6
+ uses: astral-sh/setup-uv@v7
with:
python-version: "3.11"
activate-environment: true
diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml
index bd57fdcc6..04aae5280 100644
--- a/.github/workflows/pre-commit.yml
+++ b/.github/workflows/pre-commit.yml
@@ -15,9 +15,9 @@ jobs:
timeout-minutes: 30
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v6
- name: Install UV
- uses: astral-sh/setup-uv@v6
+ uses: astral-sh/setup-uv@v7
with:
python-version: "3.12"
activate-environment: true
diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml
index ee0253837..5c8473aa5 100644
--- a/.github/workflows/unit_tests.yml
+++ b/.github/workflows/unit_tests.yml
@@ -37,12 +37,12 @@ jobs:
python-version: ["3.11", "3.12"]
runs-on: ${{ matrix.os }}
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v6
with:
submodules: recursive
- name: Install the latest version of uv
- uses: astral-sh/setup-uv@v6
+ uses: astral-sh/setup-uv@v7
with:
python-version: ${{ matrix.python-version }}
activate-environment: true
@@ -81,7 +81,7 @@ jobs:
- name: Upload test failure videos
if: failure()
- uses: actions/upload-artifact@v4
+ uses: actions/upload-artifact@v7
with:
name: playwright-videos-${{ matrix.os }}-${{ matrix.python-version }}
path: test-videos/
diff --git a/.github/workflows/whitelist.yml b/.github/workflows/whitelist.yml
index 1b17e3fcb..f38486bd7 100644
--- a/.github/workflows/whitelist.yml
+++ b/.github/workflows/whitelist.yml
@@ -10,6 +10,6 @@ jobs:
reflex-web:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v6
- name: Assert whitelist is empty
run: echo -e "\nassert not WHITELISTED_PAGES, f'WHITELISTED_PAGES includes {WHITELISTED_PAGES}'" | cat "pcweb/whitelist.py" - | python3
diff --git a/assets/custom-colors.css b/assets/custom-colors.css
index dfa230cb7..2dce83d20 100644
--- a/assets/custom-colors.css
+++ b/assets/custom-colors.css
@@ -15,6 +15,30 @@
--c-slate-10: #80838D;
--c-slate-11: #60646C;
--c-slate-12: #1C2024;
+ --slate-1: #FBFCFE;
+ --slate-2: #F7FAFD;
+ --slate-3: #EDF1F6;
+ --slate-4: #E4E9F1;
+ --slate-5: #DBE1EB;
+ --slate-6: #D3DAE6;
+ --slate-7: #C7D0DE;
+ --slate-8: #B1BDCF;
+ --slate-9: #838FA1;
+ --slate-10: #798495;
+ --slate-11: #5C6573;
+ --slate-12: #1B212A;
+ --slate-a1: #0040c004;
+ --slate-a2: #0040c008;
+ --slate-a3: #00398e12;
+ --slate-a4: #0037801c;
+ --slate-a5: #00327924;
+ --slate-a6: #002e772d;
+ --slate-a7: #002d7039;
+ --slate-a8: #0028634e;
+ --slate-a9: #00193c7c;
+ --slate-a10: #00153686;
+ --slate-a11: #001027a4;
+ --slate-a12: #000711e5;
/* Violet */
--c-violet-1: #FDFCFE;
--c-violet-2: #FAFBFF;
@@ -72,6 +96,18 @@
.dark,
.dark-theme {
/* Slate */
+ --slate-1: #141619;
+ --slate-2: #1B1D20;
+ --slate-3: #22252A;
+ --slate-4: #282B31;
+ --slate-5: #2E3238;
+ --slate-6: #353A42;
+ --slate-7: #414852;
+ --slate-8: #58616F;
+ --slate-9: #656E7D;
+ --slate-10: #737C8A;
+ --slate-11: #ADB4BF;
+ --slate-12: #ECEEF1;
/* #151618 */
--c-slate-2: #1A1B1D;
/* #1A1B1D */
@@ -115,7 +151,7 @@
--c-red-9: #E5484D;
--c-red-10: #DC3E42;
/* White */
- --c-white-1: #0E0F10;
+ --c-white-1: #1B1D20;
--glow: #261958;
--wave-line-1: #2F1C78;
--wave-line-2: #261958;
diff --git a/case-studies/ansa.md b/case-studies/ansa.md
index b3f98e2c3..1a1daf319 100644
--- a/case-studies/ansa.md
+++ b/case-studies/ansa.md
@@ -1,6 +1,8 @@
---
company: Ansa
h1: "How Ansa Uses Reflex for AI-Powered Workflow Automation"
+card_header: "How Ansa saved 100 hours of manual work a month with Reflex"
+card_description: "See how Ansa automated their fintech workflows with Reflex, eliminating 100 hours of repetitive manual work per month and accelerating their operations."
description: "Why Ansa chose Reflex over no-code and low-code frameworks for their workflow automations. Full Python control for AI-powered business process automation."
domain: "https://www.ansa.co"
founded: "New York, 2021"
@@ -42,15 +44,11 @@ meta: [
```python exec
import reflex as rx
from pcweb.constants import REFLEX_ASSETS_CDN
-from reflex_image_zoom import image_zoom
+from pcweb.pages.customers.views.app_preview_card import app_preview_card
```
```python eval
-rx.vstack(
- image_zoom(rx.image(src=f"{REFLEX_ASSETS_CDN}case_studies/ansa_app.webp", border_radius="10px", alt="Ansa App")),
- rx.text("Ansa App built with Reflex"),
- width="100%",
-)
+app_preview_card(f"{REFLEX_ASSETS_CDN}case_studies/ansa_app.webp", text="Ansa App built with Reflex")
```
Meet [Ansa](https://www.ansa.co), a venture capital firm based in New York City that invests in companies from Series A to C. They have invested in companies like Defense Unicorns, Bland, Gradient, and Selector and prior to founding the firm, supported investments in many of the venture-capital industry’s largest outcomes including Crowdstrike, Coinbase, and SurveyMonkey to name a few.
@@ -144,7 +142,8 @@ Finally, when their team has a short list of companies that fit within an invest
```md quote
- name: Ryan
- role: Investor and Head of Data
-Let’s say we have 30 companies that we want to email. How can you efficiently send a custom note to each of these companies and track it properly? We launch a script, that runs through a Reflex background event, that'll go through each company, check the CRM ownership, fill out relevant fields and find the best person to reach out to. A lot of times, especially with early stage companies, data is missing or partially complete. So this workflow will leverage LLMs throughout the process to handle fuzzy matching and make contextual decisions, as well as proactively summarize company content, news, and relevant Ansa content to help support the email writing. Before we would do this all manually, now with this new workflow in Reflex, we've taken what was once 30+ clicks across 5 different apps and made it 5x faster with 2 clicks across 2 apps.
+- variant: medium
+Let’s say we have 30 companies that we want to email. How can you efficiently send a custom note to each of these companies and track it properly? We launch a script, that runs through a Reflex background event, that’ll go through each company, check the CRM ownership, fill out relevant fields and find the best person to reach out to. A lot of times, especially with early stage companies, data is missing or partially complete. So this workflow will leverage LLMs throughout the process to handle fuzzy matching and make contextual decisions, as well as proactively summarize company content, news, and relevant Ansa content to help support the email writing. Before we would do this all manually, now with this new workflow in Reflex, we’ve taken what was once 30+ clicks across 5 different apps and made it 5x faster with 2 clicks across 2 apps.
```
All these different workflows are now built into a single Reflex app. It makes it extremely easy for anyone on the team to run any of these workflows and leverage LLM-powered automation with a few clicks.
diff --git a/case-studies/autodesk.md b/case-studies/autodesk.md
index 8797c2a31..92db29607 100644
--- a/case-studies/autodesk.md
+++ b/case-studies/autodesk.md
@@ -1,5 +1,7 @@
---
company: Autodesk
+card_header: "How Autodesk saved 25% of their development time"
+card_description: "Learn how Autodesk leveraged Reflex to streamline their internal tools development, cutting build time by 25% while maintaining enterprise-grade reliability."
description: "Streamlining Complex Workflows: Why Autodesk Chose Reflex Over Streamlit for Scalable, Python-Based Solutions"
domain: "https://autodesk.com"
founded: "San Francisco, 1982"
@@ -46,17 +48,12 @@ meta: [
```python exec
import reflex as rx
from pcweb.constants import REFLEX_ASSETS_CDN
-from reflex_image_zoom import image_zoom
-from pcweb.pages.docs import library
+from pcweb.pages.customers.views.app_preview_card import app_preview_card
```
-
+```python eval
+app_preview_card(f"{REFLEX_ASSETS_CDN}case_studies/apps/case_studies_autodesk_app.webp", text="Autodesk App built with Reflex")
+```
Meet Autodesk a globally recognized leader in design and engineering software, known for its innovative solutions that empower professionals across industries to bring their creative visions to life. Autodesk has established itself as a trusted partner for millions of users worldwide, helping them achieve greater efficiency, precision, and creativity in their projects.
@@ -130,6 +127,7 @@ The team was able to:
```md quote
- name: Paolo
- role: Principal Implementation Consultant
+- variant: medium
I am able to wear all the caps at once: Solution Architecture, UI/UX, front-end and back-end.
```
@@ -183,5 +181,6 @@ Paolo and his team have now worked on three other projects for different custome
```md quote
- name: Paolo
- role: Principal Implementation Consultant
+- variant: medium
Everything I was able to accomplish was because of the framework and unparalleled support and promptness of the Reflex team.
```
diff --git a/case-studies/bayesline.md b/case-studies/bayesline.md
index dc1cf6756..6bc2ce8eb 100644
--- a/case-studies/bayesline.md
+++ b/case-studies/bayesline.md
@@ -1,5 +1,7 @@
---
company: Bayesline
+card_header: "Why Bayesline chose Reflex over Plotly Dash"
+card_description: "Discover how Bayesline built a robust Python web application aimed at creating scalable risk models and data visualizations."
description: "Why Bayesline chose Reflex over Plotly Dash for their production-grade Python web app. Learn how they built scalable risk models and data visualizations."
domain: "https://bayesline.com/"
founded: "New York, 2024"
@@ -46,14 +48,11 @@ import reflex as rx
from pcweb.constants import REFLEX_ASSETS_CDN
from reflex_image_zoom import image_zoom
from pcweb.pages.docs import enterprise
+from pcweb.pages.customers.views.app_preview_card import app_preview_card
```
```python eval
-rx.vstack(
- image_zoom(rx.image(src=f"{REFLEX_ASSETS_CDN}case_studies/bayesline_app.webp", border_radius="10px", alt="Bayesline App")),
- rx.text("Bayesline App built with Reflex"),
- width="100%",
-)
+app_preview_card(f"{REFLEX_ASSETS_CDN}case_studies/bayesline_app.webp", text="Bayesline App built with Reflex")
```
## What Bayesline is building
@@ -70,8 +69,9 @@ Quantitative Analysts (Quants), like Sebastian, are usually proficient in data-o
Quants want to spend their time building models, proving out their ideas, and not worrying about the UI.
```md quote
-- name: Sebastian
-- role: Cofounder
+- name: Sebastian Janisch
+- role: Cofounder, Bayesline
+- image: sebastian.webp
The UI is the necessary evil, that is not our bread and butter.
```
@@ -79,16 +79,20 @@ They used to build prototypes with Dash and eventually hand them to a fully-fled
It would take months before their app ideas could be used across an organization; with Reflex you can both build and share apps in a matter of hours (`reflex deploy`).
```md quote
-- name: Sebastian
-- role: Cofounder
+- name: Sebastian Janisch
+- role: Cofounder, Bayesline
+- image: sebastian.webp
+- variant: medium
You wouldn’t want to use Dash to build a production-grade application. It’s a prototyping tool. Usually, a UX and engineering team would re-implement everything from scratch. It will take six months for anyone to get hands on it, but we want this now.
```
When embarking on building Bayesline, Sebastian and his co-founder wanted an open-source framework that would enable them to build a fully-fledged web app in pure Python, the language and ecosystem they were already familiar with.
```md quote
-- name: Sebastian
-- role: Cofounder
+- name: Sebastian Janisch
+- role: Cofounder, Bayesline
+- image: sebastian.webp
+- variant: medium
So we basically need the tool that gets us to the finish line fastest without having to learn (a new framework) and without a super steep learning curve.
```
@@ -97,7 +101,9 @@ As their app grew, it eventually became slow and difficult to maintain.
```md quote
- name: Sebastian
-- role: Cofounder
+- role: Cofounder, Bayesline
+- image: sebastian.webp
+- variant: medium
The app was just getting painfully slow. Dash loads the entire application, the entire dom of every single page. As the application gets bigger, the performance will just go down.
```
@@ -106,16 +112,20 @@ The app was just getting painfully slow. Dash loads the entire application, the
Bayesline switched from Dash to Reflex because they could build both a production-grade and an aesthetically pleasing web app quickly–without JavaScript experience.
```md quote
-- name: Sebastian
-- role: Cofounder
+- name: Sebastian Janisch
+- role: Cofounder, Bayesline
+- image: sebastian.webp
+- variant: medium
We wanted to build a frontend that would be as indistinguishable as possible from one built by professional frontend developers.
```
Complicated Dash apps not only eventually hit performance limits but are also challenging to maintain since there isn’t first-class support for object-oriented programming (OOP) design patterns.
```md quote
-- name: Sebastian
-- role: Cofounder
+- name: Sebastian Janisch
+- role: Cofounder, Bayesline
+- image: sebastian.webp
+- variant: medium
When we started looking at the (Dash) code, it just got to this point where you’re scared of it because there is no object-oriented notion; the code just turns into an enormous mess because you just have this huge collection of functions.
```
@@ -128,8 +138,10 @@ Sebastian and the team originally started building with Reflex to create a minim
They quickly learned that Reflex was already ready to build production grade web apps.
```md quote
-- name: Sebastian
-- role: Cofounder
+- name: Sebastian Janisch
+- role: Cofounder, Bayesline
+- image: sebastian.webp
+- variant: medium
Turns out I don’t see right now, as it stands at least, reasons to migrate from Reflex to somewhere else.
```
@@ -143,23 +155,29 @@ Sebastian and his small team use Reflex to build a production-grade web app pure
* Learn frontend technologies: React (JavaScript), NodeJS, TailwindCSS
```md quote
-- name: Misha and Sebastian
-- role: Cofounders
+- name: Misha Van Beek
+- role: Cofounder, Bayesline
+- image: misha.webp
+- variant: big
Reflex definitely saved us from needing to hire a frontend engineer and sped us up by 4x relative to learning React
```
* Write boilerplate to stitch together their frontend and backend (including database management)
```md quote
-- name: Sebastian
-- role: Cofounder
+- name: Sebastian Janisch
+- role: Cofounder, Bayesline
+- image: sebastian.webp
+- variant: big
50% less code than the same Dash app and easier to read / write / maintain code compared to Dash
```
* Maintain expensive, fragile, and inevitably slow Dash apps
```md quote
-- name: Misha
-- role: Cofounder
+- name: Misha Van Beek
+- role: Cofounder, Bayesline
+- image: misha.webp
+- variant: big
Using Reflex instead of Plotly Dash was like the difference between organized Legos and a plate of spaghetti
```
diff --git a/case-studies/sellerx.md b/case-studies/sellerx.md
index 6d9ed2043..39de64b91 100644
--- a/case-studies/sellerx.md
+++ b/case-studies/sellerx.md
@@ -1,5 +1,7 @@
---
company: SellerX
+card_header: "Why SellerX chose Reflex over Streamlit"
+card_description: "Find out why SellerX migrated from Streamlit to Reflex to power their e-commerce analytics platform with greater flexibility and performance."
description: "Why SellerX chose Reflex over Streamlit for their data processing pipeline. Building scalable e-commerce analytics and internal tools with Python."
domain: "https://sellerx.com"
founded: "Berlin, 2020"
@@ -39,20 +41,11 @@ meta: [
```python exec
import reflex as rx
from pcweb.constants import REFLEX_ASSETS_CDN
-from reflex_image_zoom import image_zoom
-from pcweb.pages.docs import library
+from pcweb.pages.customers.views.app_preview_card import app_preview_card
```
```python eval
-rx.vstack(
- image_zoom(rx.image(src=f"{REFLEX_ASSETS_CDN}case_studies/sellerx_app.webp", border_radius="10px", alt="SellerX App")),
- rx.text("SellerX App built with Reflex"),
- width="100%",
-)
-```
-
-```python eval
-rx.box(height="30px")
+app_preview_card(f"{REFLEX_ASSETS_CDN}case_studies/sellerx_app.webp", text="SellerX App built with Reflex")
```
@@ -141,6 +134,7 @@ The app that Mike and his team built with Reflex has been a huge success. It is
```md quote
- name: Mike
- role: Head of AI
+- variant: medium
A team of 6 non-technical employees use the app to make decisions based on Amazon information. It is allowing this team to be significantly more efficient and structured in the way they work and they are very happy with the improvements in speed. This team is now able to review 5x more Amazon data than their previous approach.
```
@@ -156,6 +150,7 @@ We are moving significantly faster, which has been very very useful. To have a q
```md quote
- name: Mike
- role: Head of AI
+- variant: medium
With Reflex it is ten times faster than developing with React and FastApi.
```
diff --git a/pcweb/docgen_pipeline.py b/pcweb/docgen_pipeline.py
index cef3fa396..de1878b76 100644
--- a/pcweb/docgen_pipeline.py
+++ b/pcweb/docgen_pipeline.py
@@ -217,7 +217,7 @@ def text_block(self, block: TextBlock) -> rx.Component:
return text_comp(text=children[0])
return rx.text(
*children,
- class_name="font-[475] text-m-slate-8 dark:text-m-slate-6 mb-4 leading-7",
+ class_name="font-[475] text-secondary-11 mb-4 leading-7",
)
def code_block(self, block: CodeBlock) -> rx.Component:
@@ -276,7 +276,7 @@ def _list_item_from_spans(spans: tuple[Span, ...]) -> rx.Component:
return list_comp(text=_spans_to_plaintext(spans))
return rx.list_item(
*_render_spans(spans),
- class_name="font-[475] text-m-slate-8 dark:text-m-slate-6 mb-4",
+ class_name="font-[475] text-secondary-11 mb-4",
)
def transform_list_item(self, item: ListItem) -> rx.Component:
@@ -515,7 +515,7 @@ def title_comp() -> rx.Component:
trigger.append(
rx.box(
self._render_children(children),
- class_name="font-[475] !text-m-slate-8 dark:!text-m-slate-6",
+ class_name="font-[475] !text-secondary-11",
),
)
body = rx.fragment()
diff --git a/pcweb/flexdown.py b/pcweb/flexdown.py
index ca764af18..c160fb6b0 100644
--- a/pcweb/flexdown.py
+++ b/pcweb/flexdown.py
@@ -1,7 +1,9 @@
import flexdown
import reflex as rx
+import reflex_ui as ui
from reflex_core.constants.colors import ColorType
+from pcweb.constants import REFLEX_ASSETS_CDN
from pcweb.styles.colors import c_color
from pcweb.styles.fonts import base, code
from pcweb.templates.docpage import (
@@ -388,31 +390,123 @@ class QuoteBlock(flexdown.blocks.MarkdownBlock):
include_indicators = True
- def render(self, env) -> rx.Component:
+ def _parse(self, env) -> dict[str, str]:
lines = self.get_lines(env)
quote_content = []
- name = ""
- role = ""
+ data = {
+ "name": "",
+ "role": "",
+ "image": "",
+ "variant": "small",
+ }
+
for line in lines[1:-1]: # Skip the first and last lines (indicators)
if line.startswith("- name:"):
- name = line.split(":", 1)[1].strip()
+ data["name"] = line.split(":", 1)[1].strip()
elif line.startswith("- role:"):
- role = line.split(":", 1)[1].strip()
+ data["role"] = line.split(":", 1)[1].strip()
+ elif line.startswith("- image:"):
+ data["image"] = line.split(":", 1)[1].strip()
+ elif line.startswith("- variant:"):
+ data["variant"] = line.split(":", 1)[1].strip().lower()
else:
quote_content.append(line)
- quote_text = "\n".join(quote_content).strip()
+ data["quote_text"] = "\n".join(quote_content).strip()
+ return data
- return rx.box(
- rx.text(f'"{quote_text}"', class_name="text-slate-11 font-base italic"),
- rx.box(
- rx.text(name, class_name="text-slate-11 font-base"),
- rx.text(role, class_name="text-slate-10 font-base"),
- class_name="flex flex-col gap-0.5",
+ def _author(self, name: str, role: str, class_name: str = "") -> rx.Component:
+ return rx.el.div(
+ rx.el.span(
+ name,
+ class_name="text-xs font-mono uppercase font-[415] text-secondary-12",
),
- class_name="flex flex-col gap-4 border-l-[3px] border-slate-4 pl-6 mt-2 mb-6",
+ rx.el.span(
+ role,
+ class_name="text-xs font-mono font-[415] text-secondary-11 uppercase",
+ ),
+ class_name=ui.cn("flex flex-col gap-0.5", class_name),
+ )
+
+ def _avatar(
+ self, name: str, image: str, class_name: str = ""
+ ) -> rx.Component | None:
+ if not image:
+ return None
+ avatar_class = ui.cn("rounded-full object-cover aspect-square", class_name)
+ return rx.image(
+ src=f"{REFLEX_ASSETS_CDN}case_studies/people/{image}",
+ alt=f"{name} profile picture",
+ class_name=avatar_class,
)
+ def _render_medium(self, data: dict[str, str]) -> rx.Component:
+ return rx.el.div(
+ rx.el.div(
+ self._avatar(data["name"], data["image"], class_name="size-6"),
+ class_name="p-4 shrink-0 lg:border-r border-secondary-8 border-dashed max-lg:border-b bg-secondary-1",
+ ),
+ rx.el.span(
+ f'"{data["quote_text"]}"',
+ class_name="text-secondary-12 text-base font-[575] p-4 bg-white-1 w-full",
+ ),
+ class_name="flex lg:flex-row flex-col border border-dashed border-secondary-8 mt-2 mb-6 rounded-lg overflow-hidden box-border bg-white-1",
+ )
+
+ def _render_small(self, data: dict[str, str]) -> rx.Component:
+ return rx.el.div(
+ rx.el.span(
+ f'"{data["quote_text"]}"',
+ class_name="text-secondary-12 text-lg font-[575] p-6 lg:border-r border-secondary-8 border-dashed max-lg:border-b bg-white-1",
+ ),
+ rx.el.div(
+ rx.el.div(
+ self._author(data["name"], data["role"]),
+ class_name="text-end text-nowrap",
+ ),
+ self._avatar(data["name"], data["image"], class_name="size-14"),
+ class_name="flex flex-row gap-6 items-center p-6 shrink-0 bg-secondary-1",
+ ),
+ class_name="flex lg:flex-row flex-col border border-dashed border-secondary-8 mt-2 mb-6 rounded-lg overflow-hidden box-border bg-white-1",
+ )
+
+ def _render_big(self, data: dict[str, str]) -> rx.Component:
+ return rx.el.div(
+ rx.el.div(
+ rx.el.span(
+ f"{data['quote_text']}",
+ class_name="text-secondary-12 text-2xl font-[575]",
+ ),
+ rx.el.div(
+ self._avatar(data["name"], data["image"], class_name="size-6"),
+ self._author(
+ data["name"],
+ data["role"],
+ class_name="flex-row gap-3.5 items-center",
+ ),
+ class_name="flex flex-row gap-3.5 items-center",
+ ),
+ class_name="flex flex-col gap-12 pr-[12.5rem] relative z-10",
+ ),
+ rx.image(
+ src=f"{REFLEX_ASSETS_CDN}common/{rx.color_mode_cond('light', 'dark')}/quote_squares.svg",
+ loading="lazy",
+ alt="Quote icon",
+ class_name="absolute right-0 inset-y-0 h-[calc(100%)] min-h-full w-auto origin-right pointer-events-none object-contain object-right",
+ ),
+ class_name="flex flex-col dark:border bg-white-1 dark:border-secondary-4 mt-2 mb-6 overflow-hidden shadow-[0_0_0_1px_rgba(0,0,0,0.12)_inset,0_6px_12px_0_rgba(0,0,0,0.06),0_1px_1px_0_rgba(0,0,0,0.01),0_4px_6px_0_rgba(0,0,0,0.02)] rounded-xl py-8 px-8 relative",
+ )
+
+ def render(self, env) -> rx.Component:
+ data = self._parse(env)
+ renderers = {
+ "small": self._render_small,
+ "medium": self._render_medium,
+ "big": self._render_big,
+ }
+ renderer = renderers.get(data["variant"], self._render_small)
+ return renderer(data)
+
class TabsBlock(flexdown.blocks.Block):
"""A block that displays content in tabs."""
diff --git a/pcweb/pages/__init__.py b/pcweb/pages/__init__.py
index e6e3735fa..e4422031a 100644
--- a/pcweb/pages/__init__.py
+++ b/pcweb/pages/__init__.py
@@ -4,8 +4,8 @@
from .blog import blog_routes
from .booked import booked as booked
from .check_your_email_demo import page_thank_you as page_thank_you
+from .customers import customers as customers
from .customers.data.customers import customers_routes
-from .customers.landing import customers as customers
from .databricks.databricks import databricks_page as databricks_page
from .demo.book_demo import book_demo as book_demo
from .docs import doc_routes
diff --git a/pcweb/pages/customers/__init__.py b/pcweb/pages/customers/__init__.py
new file mode 100644
index 000000000..e8d4425c3
--- /dev/null
+++ b/pcweb/pages/customers/__init__.py
@@ -0,0 +1,21 @@
+import reflex as rx
+
+from pcweb.pages.customers.views import book_a_demo, companies, hero
+from pcweb.templates.marketing_page import marketing_page
+
+
+@marketing_page(
+ path="/customers",
+ title="Reflex Customer Stories - Case Studies",
+ description="Case studies: Bayesline, SellerX, and Ansa use Reflex to build production Python web apps. Real success stories.",
+)
+def customers() -> rx.Component:
+ return rx.el.div(
+ hero(),
+ rx.el.hr(
+ class_name="w-full border-t border-m-slate-4 dark:border-m-slate-9",
+ ),
+ companies(),
+ book_a_demo(),
+ class_name="flex flex-col w-full justify-center items-center",
+ )
diff --git a/pcweb/pages/customers/data/customers.py b/pcweb/pages/customers/data/customers.py
index df778cec3..1790d34bb 100644
--- a/pcweb/pages/customers/data/customers.py
+++ b/pcweb/pages/customers/data/customers.py
@@ -3,53 +3,28 @@
from flexdown.document import Document
from pcweb.flexdown import xd2 as xd
-from pcweb.templates.storypage import storypage
+from pcweb.templates.storypage import CaseStudy, storypage
-
-def content(document):
- return (
- rx.box(
- xd.render(document, document.filename),
- ),
- )
+CUSTOMERS_PATH = "case-studies/"
-CUSTOMERS_PATH = "case-studies/"
+def content(document):
+ return rx.box(xd.render(document, document.filename))
def get_customer_data(paths):
customers = {}
for path in sorted(paths, reverse=True):
document = Document.from_file(path)
- path = str(path).replace(".md", "/")
- customers[path] = document
+ customers[str(path).replace(".md", "/")] = document
return customers
-def get_route(path: str):
- """Get the route for a page."""
- return path.replace(CUSTOMERS_PATH, "").replace(".md", "")
-
-
paths = flexdown.utils.get_flexdown_files(CUSTOMERS_PATH)
customer_data = get_customer_data(paths)
customers_routes = []
-for path, document in customer_data.items():
- # Get the docpage component.
- route = f"/customers/{document.metadata['company'].lower()}"
- title = rx.utils.format.to_snake_case(path.rsplit("/", 1)[1].replace(".md", ""))
- comp = storypage(
- path=route,
- company=document.metadata["company"],
- description=document.metadata["description"],
- h1=document.metadata.get("h1"),
- domain=document.metadata["domain"],
- founded=document.metadata["founded"],
- investors=document.metadata["investors"],
- stats=document.metadata["stats"],
- meta=document.metadata["meta"],
- )(lambda doc=document: content(doc))
-
- # # Add the route to the list of routes.
+for document in customer_data.values():
+ study = CaseStudy.from_document(document)
+ comp = storypage(study)(lambda doc=document: content(doc))
customers_routes.append(comp)
diff --git a/pcweb/pages/customers/landing.py b/pcweb/pages/customers/landing.py
deleted file mode 100644
index 92bf93160..000000000
--- a/pcweb/pages/customers/landing.py
+++ /dev/null
@@ -1,23 +0,0 @@
-import reflex as rx
-
-from pcweb.pages.customers.views.bento_cards import bento_cards
-from pcweb.pages.customers.views.customers_list import customers_list
-from pcweb.pages.customers.views.hero import hero
-from pcweb.pages.customers.views.stats import stats
-from pcweb.templates.mainpage import mainpage
-
-
-@mainpage(
- path="/customers",
- title="Reflex Customer Stories - Case Studies",
- description="Case studies: Bayesline, SellerX, and Ansa use Reflex to build production Python web apps. Real success stories.",
-)
-def customers() -> rx.Component:
- """Get the Customers landing page."""
- return rx.box(
- hero(),
- bento_cards(),
- stats(),
- customers_list(),
- class_name="flex flex-col w-full justify-center items-center",
- )
diff --git a/pcweb/pages/customers/views/__init__.py b/pcweb/pages/customers/views/__init__.py
new file mode 100644
index 000000000..edb3757c6
--- /dev/null
+++ b/pcweb/pages/customers/views/__init__.py
@@ -0,0 +1,11 @@
+from pcweb.pages.customers.views.book_a_demo import book_a_demo
+from pcweb.pages.customers.views.companies import companies
+from pcweb.pages.customers.views.hero import hero
+from pcweb.pages.customers.views.stats import stats
+
+__all__ = [
+ "book_a_demo",
+ "companies",
+ "hero",
+ "stats",
+]
diff --git a/pcweb/pages/customers/views/app_preview_card.py b/pcweb/pages/customers/views/app_preview_card.py
new file mode 100644
index 000000000..e4ed90079
--- /dev/null
+++ b/pcweb/pages/customers/views/app_preview_card.py
@@ -0,0 +1,39 @@
+import reflex as rx
+from reflex_image_zoom import image_zoom
+
+
+def app_preview_card(image_url: str, text: str) -> rx.Component:
+ return rx.el.div(
+ rx.el.div(
+ rx.el.div(
+ rx.el.div(
+ rx.el.div(
+ rx.el.div(
+ class_name="size-2 shadow-[0_0_0_0.5px_rgba(0,0,0,0.06)_inset] dark:shadow-[0_0_0_0.5px_rgba(255,255,255,0.06)_inset] bg-secondary-4 rounded-full"
+ ),
+ rx.el.div(
+ class_name="size-2 shadow-[0_0_0_0.5px_rgba(0,0,0,0.06)_inset] dark:shadow-[0_0_0_0.5px_rgba(255,255,255,0.06)_inset] bg-secondary-4 rounded-full"
+ ),
+ rx.el.div(
+ class_name="size-2 shadow-[0_0_0_0.5px_rgba(0,0,0,0.06)_inset] dark:shadow-[0_0_0_0.5px_rgba(255,255,255,0.06)_inset] bg-secondary-4 rounded-full"
+ ),
+ class_name="flex shrink-0 flex-row items-center gap-1",
+ ),
+ rx.el.p(
+ text,
+ class_name="absolute left-1/2 top-1/2 max-w-[calc(100%-5rem)] -translate-x-1/2 -translate-y-1/2 truncate text-center text-sm font-medium text-secondary-11",
+ ),
+ class_name="relative flex min-h-8 w-full shrink-0 flex-row items-center dark:border-b border-secondary-4 p-3 shadow-[0_0_0_1px_rgba(0,0,0,0.03),0_4px_4px_0_rgba(0,0,0,0.03),0_1px_2px_0_rgba(0,0,0,0.04)] dark:shadow-none",
+ ),
+ image_zoom(
+ rx.image(
+ src=image_url,
+ alt="App preview",
+ class_name="w-full h-full object-cover object-top h-full",
+ ),
+ ),
+ class_name="flex flex-col rounded-tl-xl w-full bg-white-1",
+ ),
+ class_name="list-inside flex flex-row relative rounded-xl bg-white-1 w-full overflow-hidden border border-black/10 dark:border-secondary-4 shadow-medium dark:shadow-none w-full mb-8",
+ ),
+ )
diff --git a/pcweb/pages/customers/views/bento_cards.py b/pcweb/pages/customers/views/bento_cards.py
deleted file mode 100644
index c75000bcf..000000000
--- a/pcweb/pages/customers/views/bento_cards.py
+++ /dev/null
@@ -1,164 +0,0 @@
-import reflex as rx
-
-from pcweb.constants import REFLEX_ASSETS_CDN
-
-
-def card(company: str, text: str, class_name: str = "") -> rx.Component:
- return rx.link(
- # Top-Left corner company logo
- # Light
- rx.image(
- src=f"{REFLEX_ASSETS_CDN}customers/light/{company}/{company}_top.svg",
- alt=f"{company} logo",
- loading="lazy",
- class_name="absolute top-10 left-10 z-[2] max-h-[32px] dark:hidden",
- ),
- # Dark
- rx.image(
- src=f"{REFLEX_ASSETS_CDN}customers/dark/{company}/{company}_top.svg",
- alt=f"{company} logo",
- loading="lazy",
- class_name="absolute top-10 left-10 z-[2] max-h-[32px] dark:block hidden",
- ),
- # Center company logo
- # Light
- rx.image(
- src=f"{REFLEX_ASSETS_CDN}customers/light/{company}/{company}_middle.svg",
- alt=f"{company} small logo",
- loading="lazy",
- class_name="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 z-[2] max-h-[88px] dark:hidden",
- ),
- # Dark
- rx.image(
- src=f"{REFLEX_ASSETS_CDN}customers/dark/{company}/{company}_middle.svg",
- alt=f"{company} small logo",
- loading="lazy",
- class_name="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 z-[2] max-h-[88px] dark:block hidden",
- ),
- # Wave pattern
- rx.html(
- """""",
- class_name="shrink-0 absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 z-[0] pointer-events-none",
- ),
- # Glowing
- rx.html(
- """""",
- class_name="shrink-0 absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 z-[1] pointer-events-none w-[15rem] h-[15rem]",
- ),
- # Text
- rx.text(
- text,
- class_name="text-slate-12 font-md-smbold absolute bottom-10 left-10 text-balance break-words max-w-[85%]",
- ),
- # Read text
- rx.box(
- rx.text(
- "Read",
- class_name="text-slate-9 font-small]",
- ),
- rx.icon(
- tag="chevron-right",
- class_name="!text-slate-9 size-3.5 group-hover:translate-x-0.5 transition-transform duration-150",
- ),
- class_name="absolute bottom-10 right-10 items-center gap-2 lg:flex hidden",
- ),
- href=f"/customers/{company.lower()}",
- class_name="rounded-[1.125rem] border border-solid border-slate-4 bg-slate-2 p-10 overflow-hidden relative h-[23.25rem] lg:shadow-large group"
- + class_name,
- )
-
-
-def bento_cards() -> rx.Component:
- return rx.el.section(
- card(
- company="autodesk",
- text="How Autodesk saved 25% of their development time on each project with Reflex",
- ),
- # Bayesline
- card(
- company="bayesline",
- text="Why Bayesline chose Reflex over Plotly Dash for their production-grade apps",
- ),
- # Ansa
- card(
- company="ansa",
- text="How Ansa saved 100 hours of manual work a month with Reflex",
- ),
- # Seller X
- card(
- company="sellerx",
- text="Why SellerX chose Reflex over Streamlit",
- ),
- class_name="grid grid-cols-1 lg:grid-cols-2 gap-4 mx-auto w-full max-w-[69.25rem]",
- )
-
-
-def _card(company: str, is_company: bool = True, **kwarg) -> rx.Component:
- return rx.link(
- # Center company logo
- rx.cond(
- is_company,
- rx.fragment(
- rx.image(
- f"{REFLEX_ASSETS_CDN}customers/light/{company}/{company}_middle.svg",
- alt=f"{company} small logo",
- loading="lazy",
- class_name="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 z-[2] max-h-[88px] dark:hidden",
- ),
- rx.image(
- f"{REFLEX_ASSETS_CDN}customers/dark/{company}/{company}_middle.svg",
- alt=f"{company} small logo",
- loading="lazy",
- class_name="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 z-[2] max-h-[88px] dark:block hidden",
- ),
- ),
- rx.image(
- **kwarg,
- alt=f"{company} small logo",
- loading="lazy",
- class_name="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 z-[2] max-h-[88px]",
- ),
- ),
- # Wave pattern
- rx.html(
- """""",
- class_name="shrink-0 absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 z-[0] pointer-events-none",
- ),
- # Glowing
- rx.html(
- """""",
- class_name="shrink-0 absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 z-[1] pointer-events-none w-[15rem] h-[15rem]",
- ),
- )
diff --git a/pcweb/pages/customers/views/book_a_demo.py b/pcweb/pages/customers/views/book_a_demo.py
new file mode 100644
index 000000000..87cdfa4a3
--- /dev/null
+++ b/pcweb/pages/customers/views/book_a_demo.py
@@ -0,0 +1,30 @@
+import reflex as rx
+from reflex_ui.blocks.demo_form import demo_form
+
+
+def book_a_demo() -> rx.Component:
+ return rx.el.section(
+ rx.el.div(
+ rx.el.div(
+ rx.el.p(
+ "Contact Us",
+ class_name="text-sm font-[525] text-primary-10 max-lg:text-center dark:text-primary-9",
+ ),
+ rx.el.h1(
+ "Book a demo",
+ class_name="text-m-slate-12 dark:text-m-slate-3 lg:text-5xl text-3xl font-[575]",
+ ),
+ rx.el.h2(
+ "Book a demo to see how you can build powerful Python web apps for your team with Reflex AI.",
+ class_name="text-m-slate-7 dark:text-m-slate-6 text-base font-[475]",
+ ),
+ class_name="flex flex-col gap-6 lg:max-w-[21rem] lg:self-start max-lg:self-center max-lg:items-center max-lg:text-center",
+ ),
+ rx.el.div(
+ demo_form(),
+ class_name="relative bg-white/96 dark:bg-m-slate-11 rounded-xl dark:border dark:border-m-slate-9 shadow-[0_0_0_1px_rgba(0,_0,_0,_0.12)_inset,_0_12px_24px_0_rgba(0,0,0,0.08),_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] dark:shadow-none w-full lg:mx-0 overflow-hidden xl:ml-12 mx-auto max-w-[42rem]",
+ ),
+ class_name="flex lg:flex-row lg:justify-between gap-12 flex-col max-lg:gap-6 max-w-(--docs-layout-max-width) mx-auto relative pb-24 max-xl:px-6",
+ ),
+ class_name="w-full relative lg:mt-24 mt-12",
+ )
diff --git a/pcweb/pages/customers/views/companies.py b/pcweb/pages/customers/views/companies.py
new file mode 100644
index 000000000..7e3824c25
--- /dev/null
+++ b/pcweb/pages/customers/views/companies.py
@@ -0,0 +1,166 @@
+from dataclasses import dataclass
+from enum import StrEnum
+
+import reflex as rx
+import reflex_ui as ui
+from reflex.experimental.client_state import ClientStateVar
+
+from pcweb.constants import REFLEX_ASSETS_CDN
+
+
+class FilterTab(StrEnum):
+ ALL = "All"
+ AI = "AI"
+ AUTH = "Auth"
+ DATA = "Data"
+ SAAS = "SaaS"
+
+
+@dataclass(frozen=True)
+class Company:
+ name: str
+ filters: tuple[FilterTab, ...]
+
+
+companies_list: list[Company] = [
+ Company(name="apple", filters=(FilterTab.AI, FilterTab.DATA)),
+ Company(name="microsoft", filters=(FilterTab.AI, FilterTab.DATA, FilterTab.SAAS)),
+ Company(name="amazon", filters=(FilterTab.AI, FilterTab.DATA, FilterTab.SAAS)),
+ Company(name="fastly", filters=(FilterTab.DATA, FilterTab.SAAS)),
+ Company(name="accenture", filters=(FilterTab.AI, FilterTab.DATA)),
+ Company(name="ibm", filters=(FilterTab.AI, FilterTab.DATA)),
+ Company(name="unicef", filters=(FilterTab.DATA,)),
+ Company(name="autodesk", filters=(FilterTab.AI, FilterTab.SAAS)),
+ Company(name="sellerx", filters=(FilterTab.AI, FilterTab.DATA)),
+ Company(name="ford", filters=(FilterTab.AI, FilterTab.DATA)),
+ Company(name="paloalto", filters=(FilterTab.AUTH, FilterTab.AI, FilterTab.DATA)),
+ Company(name="bosch", filters=(FilterTab.AI, FilterTab.DATA)),
+ Company(name="dell", filters=(FilterTab.DATA, FilterTab.SAAS)),
+ Company(name="twilio", filters=(FilterTab.AUTH, FilterTab.DATA, FilterTab.SAAS)),
+ Company(name="rappi", filters=(FilterTab.AI, FilterTab.DATA)),
+ Company(name="theworldbank", filters=(FilterTab.DATA,)),
+ Company(name="redhat", filters=(FilterTab.DATA, FilterTab.SAAS)),
+ Company(name="nike", filters=(FilterTab.AI, FilterTab.DATA)),
+]
+
+
+filter_tab_cs = ClientStateVar.create(
+ "filter_tab",
+ default=FilterTab.ALL,
+)
+
+
+def tab_item(tab: FilterTab) -> rx.Component:
+ active_cn = " 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)] dark:shadow-[0_1px_0_0_rgba(255,255,255,0.16)_inset] bg-white dark:bg-m-slate-10 hover:bg-m-slate-2 dark:hover:bg-m-slate-9 text-m-slate-12 dark:text-m-slate-3"
+ unactive_cn = " hover:text-m-slate-12 dark:hover:text-m-slate-3 text-m-slate-7 dark:text-m-slate-6"
+ return rx.el.button(
+ rx.text(tab.value, class_name="text-sm"),
+ on_click=filter_tab_cs.set_value(tab),
+ class_name=ui.cn(
+ "flex items-center cursor-pointer justify-center rounded-lg transition-colors h-7 px-2 outline-none focus:outline-none ",
+ rx.cond(filter_tab_cs.value == tab, active_cn, unactive_cn),
+ ),
+ custom_attrs={"aria-label": f"Toggle {tab} filter"},
+ )
+
+
+def companies_tabs() -> rx.Component:
+ return rx.box(
+ tab_item(FilterTab.ALL),
+ tab_item(FilterTab.AI),
+ tab_item(FilterTab.AUTH),
+ tab_item(FilterTab.DATA),
+ tab_item(FilterTab.SAAS),
+ class_name="flex flex-row gap-0.5 items-center p-0.5 [box-shadow:0_1px_0_0_rgba(0,_0,_0,_0.08),_0_0_0_1px_rgba(0,_0,_0,_0.08),_0_1px_2px_0_rgba(0,_0,_0,_0.02),_0_1px_4px_0_rgba(0,_0,_0,_0.02)] w-fit bg-m-slate-1 dark:bg-m-slate-12 rounded-[0.625rem] dark:border dark:border-m-slate-9 border border-transparent mt-2",
+ )
+
+
+def company_card(company: Company) -> rx.Component:
+ is_matching_filter = filter_tab_cs.value == FilterTab.ALL
+ for tab in company.filters:
+ is_matching_filter = is_matching_filter | (filter_tab_cs.value == tab)
+
+ return rx.box(
+ rx.box(
+ # Light
+ rx.image(
+ src=f"{REFLEX_ASSETS_CDN}landing/companies/light/{company.name}.svg",
+ class_name="w-[5.5rem] h-auto pointer-events-none group-hover:grayscale-0 grayscale-[1] dark:hidden",
+ loading="lazy",
+ filter="brightness(0) saturate(100%) invert(44%) sepia(16%) saturate(383%) hue-rotate(178deg) brightness(93%) contrast(85%)",
+ alt=f"{company.name} logo",
+ ),
+ # Dark
+ rx.image(
+ src=f"{REFLEX_ASSETS_CDN}landing/companies/dark/{company.name}.svg",
+ class_name="w-[5.5rem] h-auto pointer-events-none group-hover:grayscale-0 grayscale-[1] dark:block hidden",
+ loading="lazy",
+ filter="brightness(0) saturate(100%) invert(44%) sepia(16%) saturate(383%) hue-rotate(178deg) brightness(93%) contrast(85%)",
+ alt=f"{company.name} logo",
+ ),
+ class_name=ui.cn(
+ "w-full h-[10.75rem] flex justify-center items-center border-box transition-colors group",
+ rx.cond(is_matching_filter, "opacity-100", "opacity-30"),
+ ),
+ ),
+ class_name=(
+ "relative w-full group border-b border-r border-m-slate-4 dark:border-m-slate-9 transition-opacity",
+ ),
+ )
+
+
+def companies() -> rx.Component:
+ return rx.el.section(
+ rx.el.div(
+ rx.el.div(
+ rx.el.p(
+ "All Companies",
+ class_name="text-sm font-[525] text-primary-10 max-lg:text-center dark:text-primary-9",
+ ),
+ rx.el.h1(
+ "From startups to global enterprises, ambitious builders choose Reflex",
+ class_name="text-m-slate-12 dark:text-m-slate-3 lg:text-4xl text-3xl font-[575] shrink-0 max-w-[42rem] text-center",
+ ),
+ companies_tabs(),
+ class_name="relative flex flex-col gap-6 lg:pt-24 pt-16 justify-center items-center",
+ ),
+ rx.el.div(
+ rx.el.div(
+ class_name="absolute -top-24 left-0 w-px h-24 bg-gradient-to-t from-current to-transparent text-m-slate-4 dark:text-m-slate-10 max-lg:hidden"
+ ),
+ rx.el.div(
+ class_name="absolute -top-24 right-0 w-px h-24 bg-gradient-to-t from-current to-transparent text-m-slate-4 dark:text-m-slate-10 max-lg:hidden"
+ ),
+ rx.el.div(
+ class_name="absolute top-0 -right-24 w-24 h-px bg-gradient-to-l from-transparent to-current text-m-slate-4 dark:text-m-slate-10 max-lg:hidden"
+ ),
+ rx.el.div(
+ class_name="absolute top-0 -left-24 w-24 h-px bg-gradient-to-r from-transparent to-current text-m-slate-4 dark:text-m-slate-10 max-lg:hidden"
+ ),
+ rx.el.div(
+ class_name="absolute bottom-0 -left-24 w-24 h-px bg-gradient-to-r from-transparent to-current text-m-slate-4 dark:text-m-slate-10 max-lg:hidden"
+ ),
+ rx.el.div(
+ class_name="absolute bottom-0 -right-24 w-24 h-px bg-gradient-to-l from-transparent to-current text-m-slate-4 dark:text-m-slate-10 max-lg:hidden"
+ ),
+ rx.el.div(
+ class_name="absolute -bottom-24 left-0 w-px h-24 bg-gradient-to-b from-current to-transparent text-m-slate-4 dark:text-m-slate-10 max-lg:hidden"
+ ),
+ rx.el.div(
+ class_name="absolute -bottom-24 right-0 w-px h-24 bg-gradient-to-b from-current to-transparent text-m-slate-4 dark:text-m-slate-10 max-lg:hidden"
+ ),
+ rx.el.div(
+ *[
+ company_card(
+ company=company,
+ )
+ for company in companies_list
+ ],
+ class_name="grid grid-cols-6 w-full relative border-l border-t border-m-slate-4 dark:border-m-slate-9",
+ ),
+ class_name="relative w-full",
+ ),
+ class_name="flex flex-col justify-center items-center gap-16 max-w-(--docs-layout-max-width) relative mx-auto",
+ ),
+ class_name="bg-gradient-to-b from-white-1 to-m-slate-1 dark:from-m-slate-11 dark:to-m-slate-12 w-full lg:mb-24 mb-10 max-lg:hidden overflow-hidden",
+ )
diff --git a/pcweb/pages/customers/views/customer_cards.py b/pcweb/pages/customers/views/customer_cards.py
new file mode 100644
index 000000000..f703479a6
--- /dev/null
+++ b/pcweb/pages/customers/views/customer_cards.py
@@ -0,0 +1,117 @@
+from typing import TypedDict
+
+import reflex as rx
+
+from pcweb.components.marketing_button import button as marketing_button
+from pcweb.constants import REFLEX_ASSETS_CDN
+from pcweb.pages.customers.data.customers import customer_data
+
+
+class CustomerCard(TypedDict):
+ name: str
+ header: str
+ description: str
+ url: str
+
+
+CUSTOMERS: list[CustomerCard] = [
+ {
+ "name": doc.metadata["company"],
+ "header": doc.metadata.get(
+ "card_header",
+ doc.metadata.get("h1", doc.metadata["company"]),
+ ),
+ "description": doc.metadata.get(
+ "card_description",
+ doc.metadata.get("description", ""),
+ ),
+ "url": f"/customers/{doc.metadata['company'].lower().replace(' ', '-')}",
+ }
+ for doc in customer_data.values()
+]
+
+
+def customer_card(customer: CustomerCard) -> rx.Component:
+ return rx.el.a(
+ rx.el.div(
+ rx.el.div(
+ rx.image(
+ src=f"{REFLEX_ASSETS_CDN}case_studies/logos/{rx.color_mode_cond('light', 'dark')}/{customer['name'].lower()}_top.svg",
+ alt=customer["name"],
+ loading="lazy",
+ class_name="mb-2 max-h-6 self-start",
+ ),
+ rx.el.h3(
+ customer["header"],
+ class_name="text-m-slate-12 dark:text-m-slate-3 lg:text-2xl text-xl font-[575]",
+ ),
+ rx.el.p(
+ customer["description"],
+ class_name="text-m-slate-7 dark:text-m-slate-6 lg:text-sm text-xs font-[475]",
+ ),
+ marketing_button(
+ "Learn more",
+ variant="outline",
+ class_name="w-fit mt-auto z-1",
+ size="sm",
+ native_button=False,
+ ),
+ class_name="flex flex-col gap-4 max-w-[19rem] min-w-[19rem] lg:pb-8 pb-6 h-full",
+ ),
+ rx.el.div(
+ rx.el.div(
+ rx.el.div(
+ class_name="size-2 shadow-[0_0_0_0.5px_rgba(0,0,0,0.06)_inset] dark:shadow-[0_0_0_0.5px_rgba(255,255,255,0.06)_inset] bg-secondary-4 rounded-full"
+ ),
+ rx.el.div(
+ class_name="size-2 shadow-[0_0_0_0.5px_rgba(0,0,0,0.06)_inset] dark:shadow-[0_0_0_0.5px_rgba(255,255,255,0.06)_inset] bg-secondary-4 rounded-full"
+ ),
+ rx.el.div(
+ class_name="size-2 shadow-[0_0_0_0.5px_rgba(0,0,0,0.06)_inset] dark:shadow-[0_0_0_0.5px_rgba(255,255,255,0.06)_inset] bg-secondary-4 rounded-full"
+ ),
+ class_name="h-8 flex flex-row items-center gap-1 p-3 w-full border-b border-secondary-4",
+ ),
+ rx.image(
+ src=f"{REFLEX_ASSETS_CDN}case_studies/apps/case_studies_{customer['name'].lower().replace(' ', '_')}_app.webp",
+ alt=customer["name"],
+ class_name="w-full h-full object-cover object-top-left min-h-[24.125rem]",
+ ),
+ class_name="flex flex-col border-l border-t border-secondary-4 rounded-tl-xl w-full bg-white-1 [box-shadow:0_0_0_1px_rgba(0,_0,_0,_0.06),_0_8px_24px_rgba(0,_0,_0,_0.06),_0_24px_48px_rgba(0,_0,_0,_0.04)] dark:shadow-none",
+ ),
+ class_name="list-inside flex flex-row lg:gap-12 gap-4 lg:pl-8 lg:pt-8 pl-6 pt-6 relative rounded-xl bg-white-1 w-full overflow-hidden h-[25rem] cursor-pointer border border-black/10 dark:border-secondary-4 [box-shadow:0_6px_12px_0_rgba(0,_0,_0,_0.06),_0_1px_1px_0_rgba(0,_0,_0,_0.01),_0_4px_6px_0_rgba(0,_0,_0,_0.02)] dark:shadow-none xl:w-[42rem] w-full",
+ ),
+ to=customer["url"],
+ class_name="-m-px h-[25rem] min-w-[42rem]",
+ )
+
+
+def customer_cards() -> rx.Component:
+ return rx.el.div(
+ rx.el.div(class_name="border-b border-m-slate-4 dark:border-m-slate-9 h-12"),
+ rx.el.div(
+ *[
+ rx.fragment(
+ customer_card(customer),
+ rx.el.div(
+ class_name="border-y border-m-slate-4 dark:border-m-slate-9 h-12 last:border-b-transparent last:hidden"
+ ),
+ )
+ for customer in CUSTOMERS
+ ],
+ class_name="flex flex-col border-x border-m-slate-4 dark:border-m-slate-9",
+ ),
+ rx.el.div(class_name="border-t border-m-slate-4 dark:border-m-slate-9 h-12"),
+ rx.el.div(
+ class_name="absolute -top-12 right-0 w-px h-24 bg-gradient-to-b from-transparent to-current text-m-slate-4 dark:text-m-slate-9 max-lg:hidden"
+ ),
+ rx.el.div(
+ class_name="absolute -top-12 left-0 w-px h-24 bg-gradient-to-b from-transparent to-current text-m-slate-4 dark:text-m-slate-9 max-lg:hidden"
+ ),
+ rx.el.div(
+ class_name="absolute bottom-0 right-0 w-px h-24 bg-gradient-to-b from-current to-transparent text-m-slate-4 dark:text-m-slate-9 max-lg:hidden"
+ ),
+ rx.el.div(
+ class_name="absolute bottom-0 left-0 w-px h-24 bg-gradient-to-b from-current to-transparent text-m-slate-4 dark:text-m-slate-9 max-lg:hidden"
+ ),
+ class_name="flex flex-col w-full xl:max-w-[42rem] xl:ml-auto mt-12 pb-12 relative max-lg:overflow-hidden",
+ )
diff --git a/pcweb/pages/customers/views/customers_list.py b/pcweb/pages/customers/views/customers_list.py
deleted file mode 100644
index bd8ed7d06..000000000
--- a/pcweb/pages/customers/views/customers_list.py
+++ /dev/null
@@ -1,219 +0,0 @@
-import reflex as rx
-
-from pcweb.constants import REFLEX_ASSETS_CDN
-from pcweb.pages.docs import getting_started
-
-
-class CustomersState(rx.State):
- tags: list[str] = []
-
- @rx.event
- def add_tag(self, tag: str):
- if tag not in self.tags:
- self.tags.append(tag)
- else:
- self.tags.remove(tag)
-
- @rx.event
- def clear_tags(self):
- self.tags = []
-
-
-def tag_item(tag: str):
- return rx.box(
- rx.text(
- tag,
- class_name="font-small shrink-0",
- color=rx.cond(
- CustomersState.tags.contains(tag),
- "var(--c-white-1)",
- "var(--c-slate-9)",
- ),
- ),
- class_name="flex items-center justify-center px-3 py-1.5 cursor-pointer transition-bg shrink-0",
- background_=rx.cond(
- CustomersState.tags.contains(tag),
- "var(--c-violet-9)",
- "var(--c-slate-2)",
- ),
- _hover={
- "background": rx.cond(
- CustomersState.tags.contains(tag),
- "var(--c-violet-9)",
- "var(--c-slate-3)",
- )
- },
- on_click=CustomersState.add_tag(tag),
- )
-
-
-def all_tag():
- return rx.box(
- rx.text(
- "All",
- class_name="font-small shrink-0 text-slate-9",
- ),
- class_name="flex items-center justify-center px-3 py-1.5 cursor-pointer transition-bg shrink-0 bg-slate-1 hover:bg-slate-3",
- on_click=CustomersState.clear_tags(),
- )
-
-
-def filtering_tags():
- return rx.box(
- # Glow
- rx.html(
- """
-""",
- class_name="w-[25.25rem] h-[5.5rem] shrink-0 absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 z-[0] pointer-events-none -mt-2",
- ),
- rx.box(
- all_tag(),
- tag_item("Open Source"),
- tag_item("AI"),
- tag_item("Dev Tools"),
- tag_item("SaaS"),
- tag_item("Fintech"),
- class_name="shadow-large bg-slate-1 rounded-lg border border-slate-3 flex items-center divide-x divide-slate-3 mt-8 mb-12 relative overflow-hidden z-[1] overflow-x-auto",
- ),
- class_name="relative",
- )
-
-
-def customers_list_item(
- company: str, link: str, tag: str, has_page: bool = False
-) -> rx.Component:
- return rx.link(
- rx.box(
- rx.image(
- src=f"{REFLEX_ASSETS_CDN}customers/light/{company.lower()}/{company.lower()}_small.svg",
- alt=f"{company} logo",
- loading="lazy",
- class_name="dark:hidden h-5 w-auto shrink-0",
- ),
- rx.image(
- src=f"{REFLEX_ASSETS_CDN}customers/dark/{company.lower()}/{company.lower()}_small.svg",
- alt=f"{company} logo",
- loading="lazy",
- class_name="dark:block hidden h-5 w-auto shrink-0",
- ),
- rx.text(company, class_name="font-base font-semibold text-slate-12"),
- class_name="flex flex-row items-center gap-2.5 flex-1 justify-start",
- ),
- rx.text(
- tag, class_name="font-small-smbold text-slate-9 flex-1 flex justify-center"
- ),
- rx.cond(
- has_page,
- rx.box(
- rx.text(
- "Read",
- class_name="font-small text-slate-9",
- ),
- rx.icon(
- tag="chevron-right",
- stroke_width="2.25",
- class_name="size-3.5 !text-slate-9",
- ),
- class_name="flex flex-row items-center gap-1.5 flex-1 justify-end",
- ),
- rx.box(
- rx.text(
- "Visit",
- class_name="font-small text-slate-8",
- ),
- rx.icon(
- tag="arrow-up-right",
- stroke_width="2.25",
- class_name="size-3.5 !text-slate-8",
- ),
- class_name="flex flex-row items-center gap-1.5 flex-1 justify-end",
- ),
- ),
- href=link,
- underline="none",
- class_name="flex-row justify-between items-center w-full px-2.5 py-2 rounded-lg hover:bg-slate-3 transition-bg",
- display=rx.cond(
- CustomersState.tags.contains(tag) | (CustomersState.tags.length() == 0),
- "flex",
- "none",
- ),
- )
-
-
-def your_company_item(
- company: str, link: str, tag: str, has_page: bool = False
-) -> rx.Component:
- return rx.link(
- rx.box(
- rx.icon(
- tag="scan",
- size=21,
- stroke_width="2.5",
- class_name="!text-slate-12",
- ),
- rx.text(company, class_name="font-base font-semibold text-slate-12"),
- class_name="flex flex-row items-center gap-2.5 flex-1 justify-start",
- ),
- rx.text(
- tag, class_name="font-small-smbold text-slate-9 flex-1 flex justify-center"
- ),
- rx.box(
- rx.text(
- "Make the switch",
- class_name="font-small text-slate-12",
- ),
- rx.icon(
- tag="chevron-right",
- stroke_width="2.25",
- class_name="size-3.5 !text-slate-12",
- ),
- class_name="flex flex-row items-center gap-1.5 flex-1 justify-end",
- ),
- href=link,
- underline="none",
- class_name="flex flex-row justify-between items-center w-full px-2.5 py-2 rounded-lg hover:bg-slate-3 transition-bg",
- )
-
-
-def customers_list() -> rx.Component:
- return rx.el.section(
- # Title
- rx.box(
- rx.el.h2(
- "From startups to global enterprises,",
- class_name="font-x-large text-slate-12",
- ),
- rx.el.h3(
- "ambitious builders choose Reflex",
- class_name="font-x-large text-slate-9",
- ),
- class_name="flex flex-col justify-center items-center w-full text-center text-balance",
- ),
- # Filtering tags
- filtering_tags(),
- # Customers list
- rx.box(
- # AutoDesk
- customers_list_item("AutoDesk", "/customers/autodesk", "SaaS", True),
- # Bayesline
- customers_list_item("Bayesline", "/customers/bayesline", "Fintech", True),
- # Ansa
- customers_list_item("Ansa", "/customers/ansa", "AI", True),
- # Seller X
- customers_list_item("SellerX", "/customers/sellerx", "AI", True),
- # Dell
- customers_list_item("Dell", "https://www.dell.com/", "Dev Tools"),
- # Your company
- your_company_item("Your company", getting_started.introduction.path, ""),
- class_name="flex flex-col max-w-[40rem] justify-center w-full items-center",
- ),
- class_name="flex flex-col max-w-[64.19rem] justify-center w-full lg:border-x border-slate-3 py-12 lg:py-20 items-center",
- )
diff --git a/pcweb/pages/customers/views/hero.py b/pcweb/pages/customers/views/hero.py
index b92f181a0..92edaeefb 100644
--- a/pcweb/pages/customers/views/hero.py
+++ b/pcweb/pages/customers/views/hero.py
@@ -1,16 +1,28 @@
import reflex as rx
+from pcweb.pages.customers.views.customer_cards import customer_cards
+
def hero() -> rx.Component:
return rx.el.section(
- rx.el.h1(
- """Meet the teams
-who chose Reflex""",
- class_name="max-w-full inline-block bg-clip-text bg-gradient-to-r from-slate-12 to-slate-11 w-full font-xx-large text-center text-transparent text-balance whitespace-pre mx-auto break-words",
- ),
- rx.el.h2(
- """Companies of all sizes trust Reflex to build internal tools and customer-facing apps""",
- class_name="max-w-full w-full font-md-smbold text-center text-slate-9 whitespace-pre mx-auto text-balance word-wrap break-words",
+ rx.el.div(
+ rx.el.div(
+ rx.el.p(
+ "Customers",
+ class_name="text-sm font-[525] text-primary-10 max-lg:text-center dark:text-primary-9",
+ ),
+ rx.el.h1(
+ "Meet the teams who chose Reflex",
+ class_name="text-m-slate-12 dark:text-m-slate-3 lg:text-5xl text-3xl font-[575]",
+ ),
+ rx.el.h2(
+ "Companies of all sizes trust Reflex to build internal tools and customer-facing apps",
+ class_name="text-m-slate-7 dark:text-m-slate-6 text-base font-[475]",
+ ),
+ class_name="flex flex-col gap-6 lg:max-w-[21rem] lg:sticky mt-[5rem] lg:top-[9rem] lg:self-start max-lg:self-center max-lg:items-center max-lg:text-center",
+ ),
+ customer_cards(),
+ class_name="flex lg:flex-row flex-col max-xl:gap-6 max-w-(--docs-layout-max-width) mx-auto relative lg:pb-24 pb-6 max-xl:px-6",
),
- class_name="flex flex-col justify-center items-center gap-4 mx-auto w-full max-w-[64.19rem] lg:border-x border-slate-3 pb-[2.5rem] pt-24 lg:pt-48",
+ class_name="w-full relative",
)
diff --git a/pcweb/pages/landing/views/companies.py b/pcweb/pages/landing/views/companies.py
index 265e26df6..f20b280bf 100644
--- a/pcweb/pages/landing/views/companies.py
+++ b/pcweb/pages/landing/views/companies.py
@@ -98,8 +98,8 @@ def quote_box(company: str) -> rx.Component:
case_study = companies_case_studies_var[company]
return rx.fragment(
rx.text(
- f"“{case_study['quote']}”",
- class_name="text-xs text-slate-12 font-medium animate-fade animate-duration-[505ms] animate-ease-out animate-fill-both",
+ f"{case_study['quote']}",
+ class_name="text-xs text-slate-12 font-medium animate-fade animate-duration-[505ms] animate-ease-out animate-fill-both leading-tight",
),
rx.box(
ui.gradient_profile(
diff --git a/pcweb/templates/docpage/blocks/typography.py b/pcweb/templates/docpage/blocks/typography.py
index 652f0c7a3..04d727888 100644
--- a/pcweb/templates/docpage/blocks/typography.py
+++ b/pcweb/templates/docpage/blocks/typography.py
@@ -35,24 +35,20 @@ def definition(title: str, *children) -> rx.Component:
@rx.memo
def text_comp(text: rx.Var[str]) -> rx.Component:
- return rx.text(
- text, class_name="font-[475] text-m-slate-8 dark:text-m-slate-6 mb-4 leading-7"
- )
+ return rx.text(text, class_name="font-[475] text-secondary-11 mb-4 leading-7")
@rx.memo
def text_comp_2(text: rx.Var[str]) -> rx.Component:
return rx.text(
text,
- class_name="font-[475] text-m-slate-8 dark:text-m-slate-6 max-w-[80%] mb-10",
+ class_name="font-[475] text-secondary-11 max-w-[80%] mb-10",
)
@rx.memo
def list_comp(text: rx.Var[str]) -> rx.Component:
- return rx.list_item(
- text, class_name="font-[475] text-m-slate-8 dark:text-m-slate-6 mb-4"
- )
+ return rx.list_item(text, class_name="font-[475] text-secondary-11 mb-4")
@rx.memo
diff --git a/pcweb/templates/docpage/docpage.py b/pcweb/templates/docpage/docpage.py
index 1ead85215..6ad50c932 100644
--- a/pcweb/templates/docpage/docpage.py
+++ b/pcweb/templates/docpage/docpage.py
@@ -28,9 +28,9 @@ def right_sidebar_item_highlight():
setTimeout(() => {
const tocLinks = document.querySelectorAll('#toc-navigation a');
const activeClasses = [
- 'text-m-violet-9',
- 'dark:text-m-violet-10',
- 'shadow-[1.5px_0_0_0_var(--primary-10)_inset]',
+ 'text-primary-9',
+ 'dark:text-primary-11',
+ 'shadow-[1.5px_0_0_0_var(--primary-11)_inset]',
'dark:shadow-[1.5px_0_0_0_var(--primary-9)_inset]',
];
const defaultClasses = ['text-m-slate-7', 'dark:text-m-slate-6'];
diff --git a/pcweb/templates/storypage.py b/pcweb/templates/storypage.py
index 7ffbee259..09d5f12a3 100644
--- a/pcweb/templates/storypage.py
+++ b/pcweb/templates/storypage.py
@@ -1,296 +1,424 @@
import functools
+from dataclasses import dataclass, field
from typing import Callable
import reflex as rx
+import reflex_ui as ui
+from flexdown.document import Document
-from pcweb.components.icons.icons import get_icon
+from pcweb.components.hosting_banner import HostingBannerState
+from pcweb.components.marketing_button import button as marketing_button
from pcweb.constants import REFLEX_ASSETS_CDN
-from pcweb.pages.framework.index_colors import index_colors
from pcweb.route import Route
+from pcweb.templates.docpage import get_toc, right_sidebar_item_highlight
-def hero(
- company: str,
- description: str,
- stats: list[dict[str, str]],
- h1: str | None = None,
-) -> rx.Component:
- return rx.box(
- rx.link(
- rx.icon(
- tag="chevron-left",
- stroke_width="2.25",
- class_name="size-3.5",
- ),
- rx.text("Customer stories", class_name="font-small"),
- href="/customers",
- underline="none",
- class_name="flex items-center gap-2 text-slate-9 hover:!text-slate-11 transition-color w-fit",
- ),
- rx.el.h1(
- h1 if h1 else company,
- class_name="gradient-heading font-x-large lg:font-xx-large text-start text-transparent",
- ),
- rx.el.h2(description, class_name="text-slate-9 font-md-smbold"),
- rx.box(
- *[
- rx.box(
- rx.text(stat["value"], class_name="text-slate-12 font-x-large"),
- rx.text(stat["metric"], class_name="text-slate-9 font-small"),
- class_name="flex flex-col gap-2 mt-4",
- )
- for stat in stats
- ],
- class_name="grid grid-cols-3 gap-4 lg:gap-10",
- ),
- class_name="flex flex-col gap-4 mb-10",
- )
+@dataclass(frozen=True)
+class CaseStudy:
+ company: str
+ description: str
+ domain: str
+ founded: str
+ document: Document
+ stats: list[dict[str, str]] = field(default_factory=list)
+ meta: list[dict[str, str]] = field(default_factory=list)
+ investors: str | None = None
+ h1: str | None = None
+ @property
+ def route(self) -> str:
+ return f"/customers/{self.company.lower().replace(' ', '-')}"
+
+ @property
+ def title(self) -> str:
+ return f"{self.company} Case Study - Reflex Customer Stories"
+
+ @classmethod
+ def from_document(cls, document: Document) -> "CaseStudy":
+ m = document.metadata
+ return cls(
+ company=m["company"],
+ description=m["description"],
+ domain=m["domain"],
+ founded=m["founded"],
+ document=document,
+ stats=m["stats"],
+ meta=m.get("meta", []),
+ investors=m.get("investors"),
+ h1=m.get("h1"),
+ )
-def company_card(company: str, founded: str, investors: str, url: str) -> rx.Component:
- return rx.box(
- # Logo
+
+def gradient_logo() -> rx.Component:
+ return rx.el.div(
rx.image(
- src=rx.color_mode_cond(
- light=f"{REFLEX_ASSETS_CDN}customers/light/{company.lower()}/{company.lower()}_small.svg",
- dark=f"{REFLEX_ASSETS_CDN}customers/dark/{company.lower()}/{company.lower()}_small.svg",
- ),
- alt=f"{company} logo",
- loading="lazy",
- class_name="h-[3.5rem] w-auto shrink-0 mb-2 self-start",
- ),
- # Url
- rx.link(
- rx.text(
- url.split("//")[-1].split("/")[0], # Get the domain name formatted
- class_name="font-base truncate",
- ),
- rx.icon(
- tag="arrow-up-right",
- stroke_width="2.25",
- class_name="size-3.5",
- ),
- href=url,
- underline="none",
- is_external=True,
- class_name="flex flex-row items-center gap-1.5 text-slate-12 hover:!text-slate-10 transition-color",
+ src=f"{REFLEX_ASSETS_CDN}logos/{rx.color_mode_cond('light', 'dark')}/gradient_r.svg",
+ alt="Gradient Reflex Logo",
+ loading="eager",
+ class_name="size-full",
+ custom_attrs={"fetchPriority": "high"},
),
- # Founded
- rx.box(
- rx.text("Founded", class_name="text-slate-9 font-small-smbold"),
- rx.text(founded, class_name="text-slate-12 font-base truncate"),
- class_name="flex flex-col",
- ),
- # Investors
- rx.cond(
- investors,
- rx.box(
- rx.text("Investors", class_name="text-slate-9 font-small-smbold"),
- rx.text(investors, class_name="text-slate-12 font-base truncate"),
- class_name="flex flex-col",
- ),
- ),
- class_name="flex-col gap-4 w-[13rem] p-8 rounded-[1.125rem] border border-slate-3 bg-slate-2 z-[1] justify-start absolute right-[-6.5rem] top-[12rem] hidden xl:flex",
- is_external=True,
+ class_name="flex size-48 items-center justify-center absolute right-0 top-[15rem] left-[20.5rem] -translate-y-1/2 z-0",
)
-def more_customers(current_customer: str) -> rx.Component:
- from pcweb.pages.customers.data.customers import customer_data
-
- customer_items = list(customer_data.items())
-
- # Filter out the current company
- other_customers = [
- c for c in customer_items if c[1].metadata.get("company") != current_customer
- ]
- if not other_customers:
- return rx.box() # Return an empty box if there are no other customers
-
- # Find the index of the current company in the original list
- current_index = next(
- (
- i
- for i, (_, doc) in enumerate(customer_items)
- if doc.metadata.get("company") == current_customer
+def gradient_logo_blur_layer() -> rx.Component:
+ return rx.el.div(
+ class_name=(
+ "absolute left-[21rem] top-[15rem] -translate-y-1/2 "
+ "w-24 h-60 z-[1] bg-[rgba(252,252,253,0.01)] backdrop-blur-[36px] pointer-events-none"
),
- 0,
)
- # Get the previous and next customers
- prev_index = (current_index - 1) % len(customer_items)
- next_index = (current_index + 1) % len(customer_items)
- prev_customer = customer_items[prev_index]
- next_customer = customer_items[next_index]
-
- # Ensure we're not using the current company
- while prev_customer[1].metadata.get("company") == current_customer:
- prev_index = (prev_index - 1) % len(customer_items)
- prev_customer = customer_items[prev_index]
+def company_floating_card(company: str) -> rx.Component:
+ return rx.el.div(
+ rx.image(
+ src=f"{REFLEX_ASSETS_CDN}customers/{rx.color_mode_cond('light', 'dark')}/{company.lower()}/{company.lower()}_small.svg",
+ alt=f"{company} logo",
+ loading="eager",
+ custom_attrs={"fetchPriority": "high"},
+ class_name="w-auto h-[9.5rem]",
+ ),
+ class_name=ui.cn(
+ "size-[19rem] flex items-center justify-center absolute z-[2] rounded-[5rem] backdrop-blur-[36px] bg-linear-to-b from-m-slate-1 to-m-slate-2 dark:from-m-slate-11 dark:to-m-slate-12 shadow-[0_1px_0_0_#FFF_inset,_0_0_0_1px_rgba(0,_0,_0,_0.12),_0_16px_32px_0_rgba(0,_0,_0,_0.06),_0_1px_1px_0_rgba(0,_0,_0,_0.01),_0_8px_16px_0_rgba(0,_0,_0,_0.02)] top-[5.5rem] left-[3.5rem] dark:shadow-none dark:border dark:border-m-slate-9",
+ ),
+ )
- while next_customer[1].metadata.get("company") == current_customer:
- next_index = (next_index + 1) % len(customer_items)
- next_customer = customer_items[next_index]
- customers = [
- rx.box(
- rx.link(
- rx.box(
- "Previous",
- get_icon(icon="arrow_right", class_name="rotate-180"),
- class_name="flex flex-row-reverse justify-center lg:justify-start items-center gap-2 rounded-lg w-full self-end",
+def hero(study: CaseStudy) -> rx.Component:
+ return rx.el.div(
+ rx.el.div(
+ rx.el.div(
+ rx.el.a(
+ marketing_button(
+ "Customers",
+ icon="ArrowRight01Icon",
+ native_button=False,
+ variant="ghost",
+ size="xs",
+ ),
+ to="/customers",
+ ),
+ ui.icon(
+ "ArrowRight01Icon",
+ class_name="text-m-slate-7 dark:text-m-slate-6",
),
- underline="none",
- href=f"/customers/{prev_customer[1].metadata['company'].lower()}",
- class_name="py-0.5 lg:py-0 rounded-lg lg:w-auto font-small text-slate-9 hover:!text-slate-11 transition-color",
+ rx.el.span(
+ study.company,
+ class_name="text-m-slate-7 dark:text-m-slate-6 text-sm font-[525]",
+ ),
+ class_name="flex items-center gap-3",
+ ),
+ rx.el.h1(
+ study.h1 or study.company,
+ class_name="text-m-slate-12 dark:text-m-slate-3 lg:text-5xl text-4xl font-[575] text-start",
),
- rx.box(
+ rx.el.h2(
+ study.description,
+ class_name="text-m-slate-7 dark:text-m-slate-6 text-base font-[475] text-balance",
+ ),
+ class_name="flex flex-col gap-6 xl:max-w-[30rem] justify-start pt-24 xl:pb-34 pb-16 z-1 xl:min-h-[30rem] max-xl:px-6",
+ ),
+ rx.el.div(
+ rx.el.div(
rx.image(
- src=rx.color_mode_cond(
- light=f"{REFLEX_ASSETS_CDN}customers/light/{prev_customer[1].metadata['company'].lower()}/{prev_customer[1].metadata['company'].lower()}_small.svg",
- dark=f"{REFLEX_ASSETS_CDN}customers/dark/{prev_customer[1].metadata['company'].lower()}/{prev_customer[1].metadata['company'].lower()}_small.svg",
+ src=f"{REFLEX_ASSETS_CDN}common/{rx.color_mode_cond('light', 'dark')}/grid.svg",
+ alt="Grid",
+ loading="eager",
+ custom_attrs={"fetchPriority": "high"},
+ class_name=ui.cn(
+ "absolute -right-22 top-0 z-[-1] w-[45rem] h-[27rem] max-w-none pointer-events-none",
),
- alt=f"{next_customer[1].metadata['company']} logo",
- loading="lazy",
- class_name="h-[1.25rem] w-auto",
- ),
- rx.text(
- prev_customer[1].metadata["company"],
- class_name="font-smbold text-slate-12",
),
- class_name="flex flex-row justify-start gap-2.5 items-center",
+ gradient_logo(),
+ company_floating_card(study.company),
+ class_name="relative isolate h-full w-full",
),
- class_name="flex flex-col justify-start gap-1 items-start",
+ class_name="flex-1 max-xl:hidden",
),
- rx.box(
- rx.link(
- rx.box(
- "Next",
- get_icon(icon="arrow_right"),
- class_name="flex flex-row justify-center lg:justify-start items-center gap-2 rounded-lg w-full self-end",
+ class_name="flex flex-row lg:px-4 max-w-(--docs-layout-max-width) w-full mx-auto border-b border-m-slate-4 dark:border-m-slate-9 relative",
+ )
+
+
+def stats_cards(study: CaseStudy) -> rx.Component:
+ return rx.el.div(
+ rx.el.div(
+ *[
+ rx.el.div(
+ rx.el.span(
+ stat["value"],
+ class_name="text-m-slate-12 dark:text-m-slate-3 lg:text-6xl text-3xl font-[415] font-mono",
+ ),
+ rx.el.span(
+ stat["metric"],
+ class_name="text-m-slate-7 dark:text-m-slate-6 text-xs font-[475]",
+ ),
+ class_name="flex flex-col gap-2",
+ )
+ for stat in study.stats
+ ],
+ class_name="grid lg:grid-cols-3 grid-cols-1 gap-4 lg:gap-12 border-r border-m-slate-4 dark:border-m-slate-9 pt-8 xl:pr-12 pr-8 max-xl:pl-4 max-xl:pb-8",
+ ),
+ rx.el.div(
+ rx.el.div(
+ rx.el.span(
+ "Founded",
+ class_name="text-m-slate-7 dark:text-m-slate-6 text-xs font-[415] font-mono uppercase",
+ ),
+ rx.el.span(
+ study.founded,
+ class_name="text-m-slate-12 dark:text-m-slate-3 lg:text-base text-sm font-[415] uppercase font-mono",
),
- underline="none",
- href=f"/customers/{next_customer[1].metadata['company'].lower()}",
- class_name="py-0.5 lg:py-0 rounded-lg lg:w-auto font-small text-slate-9 hover:!text-slate-11 transition-color",
+ class_name="flex flex-col gap-1",
),
- rx.box(
- rx.text(
- next_customer[1].metadata["company"],
- class_name="font-smbold text-slate-12",
+ rx.cond(
+ study.investors,
+ rx.el.div(
+ rx.el.span(
+ "Investors",
+ class_name="text-m-slate-7 dark:text-m-slate-6 text-xs font-[415] font-mono uppercase",
+ ),
+ rx.el.span(
+ study.investors,
+ class_name="text-m-slate-12 dark:text-m-slate-3 lg:text-base text-xs font-[415] uppercase font-mono",
+ ),
+ class_name="flex flex-col gap-1",
),
- rx.image(
- src=rx.color_mode_cond(
- light=f"{REFLEX_ASSETS_CDN}customers/light/{next_customer[1].metadata['company'].lower()}/{next_customer[1].metadata['company'].lower()}_small.svg",
- dark=f"{REFLEX_ASSETS_CDN}customers/dark/{next_customer[1].metadata['company'].lower()}/{next_customer[1].metadata['company'].lower()}_small.svg",
+ ),
+ rx.el.div(
+ rx.el.span(
+ "Website",
+ class_name="text-m-slate-7 dark:text-m-slate-6 text-xs font-[415] font-mono uppercase",
+ ),
+ rx.el.a(
+ rx.el.span(
+ study.domain.split("//")[-1].split("/")[0].replace("www.", ""),
+ class_name="text-xs font-[415] uppercase font-mono",
+ ),
+ rx.icon(
+ tag="arrow-up-right",
+ stroke_width="2.25",
+ class_name="size-3.5",
),
- alt=f"{next_customer[1].metadata['company']} logo",
- loading="lazy",
- class_name="h-[1.25rem] w-auto",
+ to=study.domain,
+ underline="none",
+ target="_blank",
+ class_name="flex flex-row items-center gap-1.5 underline underline-offset-1 text-m-slate-12 dark:text-m-slate-3 ",
),
- class_name="flex flex-row justify-start gap-2.5 items-center",
+ class_name="flex flex-col gap-1",
),
- class_name="flex flex-col justify-start gap-1 items-end",
+ class_name="flex flex-col gap-4 p-8 xl:w-[21rem] shrink-0",
+ ),
+ rx.el.div(
+ class_name="absolute -top-px -right-24 w-24 h-px bg-gradient-to-l from-transparent to-current text-m-slate-4 dark:text-m-slate-10 max-lg:hidden"
+ ),
+ rx.el.div(
+ class_name="absolute -top-px -left-24 w-24 h-px bg-gradient-to-r from-transparent to-current text-m-slate-4 dark:text-m-slate-10 max-lg:hidden"
+ ),
+ rx.el.div(
+ class_name="absolute -bottom-px -left-24 w-24 h-px bg-gradient-to-r from-transparent to-current text-m-slate-4 dark:text-m-slate-10 max-lg:hidden"
),
- ]
+ rx.el.div(
+ class_name="absolute -bottom-px -right-24 w-24 h-px bg-gradient-to-l from-transparent to-current text-m-slate-4 dark:text-m-slate-10 max-lg:hidden"
+ ),
+ class_name="flex flex-row max-w-(--docs-layout-max-width) w-full mx-auto border-b border-m-slate-4 dark:border-m-slate-9 relative max-2xl:overflow-hidden",
+ )
+
- return rx.box(
+def story_table_of_contents(toc: list, path: str, company: str) -> rx.Component:
+ """Render the table of contents sidebar for a case study page."""
+ if len(toc) < 2:
+ return rx.fragment()
+
+ return rx.el.nav(
+ rx.el.div(
+ class_name="absolute -top-8 left-0 w-px h-8 bg-gradient-to-b from-transparent to-current text-m-slate-4 dark:text-m-slate-9 max-lg:hidden"
+ ),
rx.box(
- *customers,
- class_name="flex flex-row gap-4 justify-between items-center",
+ rx.el.ul(
+ rx.el.li(
+ f"Reflex X {company}",
+ class_name="text-xs flex items-center justify-start font-[415] dark:text-m-slate-3 text-m-slate-12 font-mono uppercase pb-6 pl-8",
+ ),
+ *[
+ rx.el.li(
+ rx.el.a(
+ text,
+ class_name=ui.cn(
+ "text-sm font-[525] text-m-slate-7 dark:text-m-slate-6 py-1 block hover:text-m-slate-9 dark:hover:text-m-slate-5 transition-colors truncate",
+ "pl-8" if level <= 2 else "pl-12",
+ ),
+ href=path + "#" + text.lower().replace(" ", "-"),
+ ),
+ )
+ for level, text in toc
+ ],
+ id="toc-navigation",
+ class_name="flex flex-col gap-y-1 list-none shadow-[1px_0_0_0_var(--m-slate-4)_inset] dark:shadow-[1px_0_0_0_var(--m-slate-9)_inset]",
+ ),
+ class_name="flex flex-col justify-start gap-y-4 overflow-y-auto",
+ ),
+ on_mount=rx.call_script(right_sidebar_item_highlight()),
+ class_name=ui.cn(
+ "sticky w-[21.05rem] shrink-0 hidden xl:block self-start max-lg:hidden",
+ rx.cond(
+ HostingBannerState.is_banner_visible,
+ "top-[8.5rem]",
+ "top-[6.5rem]",
+ ),
),
- class_name="flex flex-col gap-6 mt-20",
)
-def storypage(
- path: str,
- description: str,
- company: str,
- h1: str | None = None,
- domain: str | None = None,
- founded: str | None = None,
- investors: str | None = None,
- stats: list[dict[str, str]] | None = None,
- meta: list[dict[str, str]] | None = None,
- props=None,
- add_as_page=True,
-) -> 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.
- description: The description of the page.
- company: The company name.
- h1: Optional H1 for the hero. Used when company name alone is too short for SEO.
- domain: The company domain.
- founded: The company founded date.
- investors: The company investors.
- stats: The company stats to show in the hero.
- 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.
-
- Returns:
- A wrapper function that returns the full webpage.
- """
- props = props or {}
+CAROUSEL_SCROLL_JS = """
+(function(direction) {
+ const container = document.getElementById('more-customers-carousel');
+ if (!container) return;
+ const cards = Array.from(container.querySelectorAll('a'));
+ if (!cards.length) return;
+ const gap = 24;
+ const containerLeft = container.getBoundingClientRect().left;
+ const paddingLeft = parseFloat(getComputedStyle(container).paddingLeft) || 0;
- def storypage(contents: Callable[[], Route]) -> Route:
- """Wrapper to create a templated route.
+ // Find the current card index based on scroll position
+ let currentIdx = 0;
+ for (let i = 0; i < cards.length; i++) {
+ const cardLeft = cards[i].getBoundingClientRect().left - containerLeft - paddingLeft;
+ if (cardLeft >= -10) { currentIdx = i; break; }
+ }
- Args:
- contents: The function to create the page route.
+ const targetIdx = Math.max(0, Math.min(cards.length - 1, currentIdx + direction));
+ const targetScroll = targetIdx * (cards[0].offsetWidth + gap);
+ container.scrollTo({ left: targetScroll, behavior: 'smooth' });
+})
+"""
- Returns:
- The templated route.
- """
+CAROUSEL_FADE_INIT_JS = """
+(function() {
+ const container = document.getElementById('more-customers-carousel');
+ const fadeL = document.getElementById('carousel-fade-left');
+ const fadeR = document.getElementById('carousel-fade-right');
+ if (!container || !fadeL || !fadeR) return;
- @functools.wraps(contents)
- def wrapper(*children, **props) -> rx.Component:
- """The template component.
+ function update() {
+ const sl = container.scrollLeft;
+ const maxScroll = container.scrollWidth - container.clientWidth;
+ fadeL.style.opacity = '1';
+ fadeR.style.opacity = sl >= maxScroll - 2 ? '0' : '1';
+ }
+
+ container.addEventListener('scroll', update);
+ update();
+})()
+"""
+
+
+def more_customers(current_customer: str) -> rx.Component:
+ from pcweb.pages.customers.views.customer_cards import CUSTOMERS, customer_card
+
+ others = [c for c in CUSTOMERS if c["name"] != current_customer]
+ if not others:
+ return rx.fragment()
+
+ return rx.el.div(
+ rx.el.div(
+ rx.el.h3(
+ "See what other teams built",
+ class_name="text-m-slate-12 dark:text-m-slate-3 lg:text-3xl text-xl font-[575]",
+ ),
+ rx.el.div(
+ ui.button(
+ ui.icon("ArrowLeft01Icon", class_name="size-4"),
+ on_click=rx.call_script(f"{CAROUSEL_SCROLL_JS}(-1)"),
+ variant="outline-shadow",
+ size="sm",
+ ),
+ ui.button(
+ ui.icon("ArrowRight01Icon", class_name="size-4"),
+ on_click=rx.call_script(f"{CAROUSEL_SCROLL_JS}(1)"),
+ variant="outline-shadow",
+ size="sm",
+ ),
+ class_name="flex flex-row items-center gap-2",
+ ),
+ class_name="flex flex-row items-center justify-between w-full max-w-(--docs-layout-max-width) mx-auto max-xl:px-6",
+ ),
+ rx.el.div(
+ rx.el.div(
+ *[customer_card(customer) for customer in others],
+ id="more-customers-carousel",
+ class_name="flex flex-row gap-6 overflow-x-auto scroll-smooth [scrollbar-width:none] [&::-webkit-scrollbar]:hidden pl-[6rem] pr-8 py-8 -my-8 max-lg:pl-6 max-lg:pr-6",
+ ),
+ rx.el.div(
+ id="carousel-fade-left",
+ class_name="absolute left-0 inset-y-0 w-24 bg-gradient-to-r from-m-slate-1 dark:from-m-slate-12 to-transparent pointer-events-none z-10 transition-opacity",
+ ),
+ rx.el.div(
+ id="carousel-fade-right",
+ class_name="absolute right-0 inset-y-0 w-24 bg-gradient-to-l from-m-slate-1 dark:from-m-slate-12 to-transparent pointer-events-none z-10 transition-opacity",
+ ),
+ class_name="relative",
+ ),
+ rx.script(CAROUSEL_FADE_INIT_JS),
+ class_name="flex flex-col gap-8 lg:mt-20 mt-6 lg:mb-24 mb-12 max-w-[81rem] w-full mx-auto",
+ )
- 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.pages.framework.views.divider import divider
+def storypage(study: CaseStudy, add_as_page: bool = True) -> Callable:
+ """A template that wraps a case study page with navbar, hero, stats, TOC, and footer."""
+ toc_raw, _ = get_toc(study.document, study.route)
+ toc = [(level, text) for level, text in toc_raw if level <= 3]
+
+ def storypage(contents: Callable[[], Route]) -> Route:
+ @functools.wraps(contents)
+ def wrapper(*children, **props) -> rx.Component:
+ from pcweb.pages.customers.views.book_a_demo import book_a_demo
from pcweb.pages.framework.views.footer_index import footer_index
from pcweb.views.marketing_navbar import marketing_navbar
- # Wrap the component in the template.
- return rx.box(
- rx.box(
- index_colors(),
- marketing_navbar(),
- company_card(company, founded, investors, domain),
- rx.el.main(
- hero(company, description, stats, h1),
- contents(*children, **props),
- more_customers(company),
- rx.box(class_name="flex-grow"),
- class_name="w-full z-[1] relative flex flex-col justify-center mx-auto max-w-[640px] lg:px-0 px-4 pb-20",
+ return rx.el.div(
+ marketing_navbar(),
+ rx.el.main(
+ rx.el.div(
+ hero(study),
+ stats_cards(study),
+ rx.el.div(
+ rx.el.div(
+ contents(*children, **props),
+ class_name="flex flex-col gap-4 flex-1 xl:max-w-2xl w-full",
+ ),
+ story_table_of_contents(toc, study.route, study.company),
+ class_name="flex flex-row gap-24 max-w-(--docs-layout-max-width) mx-auto w-full lg:pb-24 pb-12 pt-8 max-lg:px-6 justify-between",
+ ),
+ more_customers(study.company),
+ rx.el.hr(
+ class_name="w-full border-t border-m-slate-4 dark:border-m-slate-9",
+ ),
+ rx.el.div(
+ book_a_demo(),
+ class_name="bg-gradient-to-b from-white-1 to-m-slate-1 dark:from-m-slate-11 dark:to-m-slate-12 w-full mb-10 max-lg:hidden",
+ ),
+ footer_index(),
+ 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",
+ ),
),
- rx.box(class_name="h-[1px] bg-slate-3 w-full"),
- class_name="relative flex flex-col justify-start items-center w-full h-full min-h-screen font-instrument-sans gap-4 mx-auto max-w-[64.19rem] lg:border-x border-slate-3 pt-24 lg:pt-48",
),
- divider(),
- footer_index(),
- class_name="relative overflow-hidden flex flex-col justify-center items-center w-full",
- **props,
+ class_name="flex flex-col w-full justify-center items-center relative bg-secondary-1",
)
return Route(
- path=path,
- title=company + " Case Study - Reflex Customer Stories",
- description=description,
- meta=meta,
+ path=study.route,
+ title=study.title,
+ description=study.description,
+ meta=study.meta,
component=wrapper,
add_as_page=add_as_page,
)
diff --git a/pcweb/views/marketing_navbar.py b/pcweb/views/marketing_navbar.py
index 61c656e45..8a4e4f426 100644
--- a/pcweb/views/marketing_navbar.py
+++ b/pcweb/views/marketing_navbar.py
@@ -19,7 +19,7 @@
from pcweb.pages.about import about_page
from pcweb.pages.blog import blogs
from pcweb.pages.blog.paths import blog_data_visible
-from pcweb.pages.customers.landing import customers
+from pcweb.pages.customers import customers
from pcweb.pages.docs import ai_builder
from pcweb.pages.faq import faq
from pcweb.pages.framework.framework import framework
diff --git a/uv.lock b/uv.lock
index 0aa422313..bb1034de9 100644
--- a/uv.lock
+++ b/uv.lock
@@ -21,7 +21,7 @@ wheels = [
[[package]]
name = "aiohttp"
-version = "3.13.4"
+version = "3.13.5"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "aiohappyeyeballs" },
@@ -32,93 +32,93 @@ dependencies = [
{ name = "propcache" },
{ name = "yarl" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/45/4a/064321452809dae953c1ed6e017504e72551a26b6f5708a5a80e4bf556ff/aiohttp-3.13.4.tar.gz", hash = "sha256:d97a6d09c66087890c2ab5d49069e1e570583f7ac0314ecf98294c1b6aaebd38", size = 7859748, upload-time = "2026-03-28T17:19:40.6Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/d4/7e/cb94129302d78c46662b47f9897d642fd0b33bdfef4b73b20c6ced35aa4c/aiohttp-3.13.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8ea0c64d1bcbf201b285c2246c51a0c035ba3bbd306640007bc5844a3b4658c1", size = 760027, upload-time = "2026-03-28T17:15:33.022Z" },
- { url = "https://files.pythonhosted.org/packages/5e/cd/2db3c9397c3bd24216b203dd739945b04f8b87bb036c640da7ddb63c75ef/aiohttp-3.13.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6f742e1fa45c0ed522b00ede565e18f97e4cf8d1883a712ac42d0339dfb0cce7", size = 508325, upload-time = "2026-03-28T17:15:34.714Z" },
- { url = "https://files.pythonhosted.org/packages/36/a3/d28b2722ec13107f2e37a86b8a169897308bab6a3b9e071ecead9d67bd9b/aiohttp-3.13.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6dcfb50ee25b3b7a1222a9123be1f9f89e56e67636b561441f0b304e25aaef8f", size = 502402, upload-time = "2026-03-28T17:15:36.409Z" },
- { url = "https://files.pythonhosted.org/packages/fa/d6/acd47b5f17c4430e555590990a4746efbcb2079909bb865516892bf85f37/aiohttp-3.13.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3262386c4ff370849863ea93b9ea60fd59c6cf56bf8f93beac625cf4d677c04d", size = 1771224, upload-time = "2026-03-28T17:15:38.223Z" },
- { url = "https://files.pythonhosted.org/packages/98/af/af6e20113ba6a48fd1cd9e5832c4851e7613ef50c7619acdaee6ec5f1aff/aiohttp-3.13.4-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:473bb5aa4218dd254e9ae4834f20e31f5a0083064ac0136a01a62ddbae2eaa42", size = 1731530, upload-time = "2026-03-28T17:15:39.988Z" },
- { url = "https://files.pythonhosted.org/packages/81/16/78a2f5d9c124ad05d5ce59a9af94214b6466c3491a25fb70760e98e9f762/aiohttp-3.13.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e56423766399b4c77b965f6aaab6c9546617b8994a956821cc507d00b91d978c", size = 1827925, upload-time = "2026-03-28T17:15:41.944Z" },
- { url = "https://files.pythonhosted.org/packages/2a/1f/79acf0974ced805e0e70027389fccbb7d728e6f30fcac725fb1071e63075/aiohttp-3.13.4-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8af249343fafd5ad90366a16d230fc265cf1149f26075dc9fe93cfd7c7173942", size = 1923579, upload-time = "2026-03-28T17:15:44.071Z" },
- { url = "https://files.pythonhosted.org/packages/af/53/29f9e2054ea6900413f3b4c3eb9d8331f60678ec855f13ba8714c47fd48d/aiohttp-3.13.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0bc0a5cf4f10ef5a2c94fdde488734b582a3a7a000b131263e27c9295bd682d9", size = 1767655, upload-time = "2026-03-28T17:15:45.911Z" },
- { url = "https://files.pythonhosted.org/packages/f3/57/462fe1d3da08109ba4aa8590e7aed57c059af2a7e80ec21f4bac5cfe1094/aiohttp-3.13.4-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5c7ff1028e3c9fc5123a865ce17df1cb6424d180c503b8517afbe89aa566e6be", size = 1630439, upload-time = "2026-03-28T17:15:48.11Z" },
- { url = "https://files.pythonhosted.org/packages/d7/4b/4813344aacdb8127263e3eec343d24e973421143826364fa9fc847f6283f/aiohttp-3.13.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ba5cf98b5dcb9bddd857da6713a503fa6d341043258ca823f0f5ab7ab4a94ee8", size = 1745557, upload-time = "2026-03-28T17:15:50.13Z" },
- { url = "https://files.pythonhosted.org/packages/d4/01/1ef1adae1454341ec50a789f03cfafe4c4ac9c003f6a64515ecd32fe4210/aiohttp-3.13.4-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:d85965d3ba21ee4999e83e992fecb86c4614d6920e40705501c0a1f80a583c12", size = 1741796, upload-time = "2026-03-28T17:15:52.351Z" },
- { url = "https://files.pythonhosted.org/packages/22/04/8cdd99af988d2aa6922714d957d21383c559835cbd43fbf5a47ddf2e0f05/aiohttp-3.13.4-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:49f0b18a9b05d79f6f37ddd567695943fcefb834ef480f17a4211987302b2dc7", size = 1805312, upload-time = "2026-03-28T17:15:54.407Z" },
- { url = "https://files.pythonhosted.org/packages/fb/7f/b48d5577338d4b25bbdbae35c75dbfd0493cb8886dc586fbfb2e90862239/aiohttp-3.13.4-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:7f78cb080c86fbf765920e5f1ef35af3f24ec4314d6675d0a21eaf41f6f2679c", size = 1621751, upload-time = "2026-03-28T17:15:56.564Z" },
- { url = "https://files.pythonhosted.org/packages/bc/89/4eecad8c1858e6d0893c05929e22343e0ebe3aec29a8a399c65c3cc38311/aiohttp-3.13.4-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:67a3ec705534a614b68bbf1c70efa777a21c3da3895d1c44510a41f5a7ae0453", size = 1826073, upload-time = "2026-03-28T17:15:58.489Z" },
- { url = "https://files.pythonhosted.org/packages/f5/5c/9dc8293ed31b46c39c9c513ac7ca152b3c3d38e0ea111a530ad12001b827/aiohttp-3.13.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d6630ec917e85c5356b2295744c8a97d40f007f96a1c76bf1928dc2e27465393", size = 1760083, upload-time = "2026-03-28T17:16:00.677Z" },
- { url = "https://files.pythonhosted.org/packages/1e/19/8bbf6a4994205d96831f97b7d21a0feed120136e6267b5b22d229c6dc4dc/aiohttp-3.13.4-cp311-cp311-win32.whl", hash = "sha256:54049021bc626f53a5394c29e8c444f726ee5a14b6e89e0ad118315b1f90f5e3", size = 439690, upload-time = "2026-03-28T17:16:02.902Z" },
- { url = "https://files.pythonhosted.org/packages/0c/f5/ac409ecd1007528d15c3e8c3a57d34f334c70d76cfb7128a28cffdebd4c1/aiohttp-3.13.4-cp311-cp311-win_amd64.whl", hash = "sha256:c033f2bc964156030772d31cbf7e5defea181238ce1f87b9455b786de7d30145", size = 463824, upload-time = "2026-03-28T17:16:05.058Z" },
- { url = "https://files.pythonhosted.org/packages/1e/bd/ede278648914cabbabfdf95e436679b5d4156e417896a9b9f4587169e376/aiohttp-3.13.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:ee62d4471ce86b108b19c3364db4b91180d13fe3510144872d6bad5401957360", size = 752158, upload-time = "2026-03-28T17:16:06.901Z" },
- { url = "https://files.pythonhosted.org/packages/90/de/581c053253c07b480b03785196ca5335e3c606a37dc73e95f6527f1591fe/aiohttp-3.13.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c0fd8f41b54b58636402eb493afd512c23580456f022c1ba2db0f810c959ed0d", size = 501037, upload-time = "2026-03-28T17:16:08.82Z" },
- { url = "https://files.pythonhosted.org/packages/fa/f9/a5ede193c08f13cc42c0a5b50d1e246ecee9115e4cf6e900d8dbd8fd6acb/aiohttp-3.13.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4baa48ce49efd82d6b1a0be12d6a36b35e5594d1dd42f8bfba96ea9f8678b88c", size = 501556, upload-time = "2026-03-28T17:16:10.63Z" },
- { url = "https://files.pythonhosted.org/packages/d6/10/88ff67cd48a6ec36335b63a640abe86135791544863e0cfe1f065d6cef7a/aiohttp-3.13.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d738ebab9f71ee652d9dbd0211057690022201b11197f9a7324fd4dba128aa97", size = 1757314, upload-time = "2026-03-28T17:16:12.498Z" },
- { url = "https://files.pythonhosted.org/packages/8b/15/fdb90a5cf5a1f52845c276e76298c75fbbcc0ac2b4a86551906d54529965/aiohttp-3.13.4-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0ce692c3468fa831af7dceed52edf51ac348cebfc8d3feb935927b63bd3e8576", size = 1731819, upload-time = "2026-03-28T17:16:14.558Z" },
- { url = "https://files.pythonhosted.org/packages/ec/df/28146785a007f7820416be05d4f28cc207493efd1e8c6c1068e9bdc29198/aiohttp-3.13.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8e08abcfe752a454d2cb89ff0c08f2d1ecd057ae3e8cc6d84638de853530ebab", size = 1793279, upload-time = "2026-03-28T17:16:16.594Z" },
- { url = "https://files.pythonhosted.org/packages/10/47/689c743abf62ea7a77774d5722f220e2c912a77d65d368b884d9779ef41b/aiohttp-3.13.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5977f701b3fff36367a11087f30ea73c212e686d41cd363c50c022d48b011d8d", size = 1891082, upload-time = "2026-03-28T17:16:18.71Z" },
- { url = "https://files.pythonhosted.org/packages/b0/b6/f7f4f318c7e58c23b761c9b13b9a3c9b394e0f9d5d76fbc6622fa98509f6/aiohttp-3.13.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:54203e10405c06f8b6020bd1e076ae0fe6c194adcee12a5a78af3ffa3c57025e", size = 1773938, upload-time = "2026-03-28T17:16:21.125Z" },
- { url = "https://files.pythonhosted.org/packages/aa/06/f207cb3121852c989586a6fc16ff854c4fcc8651b86c5d3bd1fc83057650/aiohttp-3.13.4-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:358a6af0145bc4dda037f13167bef3cce54b132087acc4c295c739d05d16b1c3", size = 1579548, upload-time = "2026-03-28T17:16:23.588Z" },
- { url = "https://files.pythonhosted.org/packages/6c/58/e1289661a32161e24c1fe479711d783067210d266842523752869cc1d9c2/aiohttp-3.13.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:898ea1850656d7d61832ef06aa9846ab3ddb1621b74f46de78fbc5e1a586ba83", size = 1714669, upload-time = "2026-03-28T17:16:25.713Z" },
- { url = "https://files.pythonhosted.org/packages/96/0a/3e86d039438a74a86e6a948a9119b22540bae037d6ba317a042ae3c22711/aiohttp-3.13.4-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:7bc30cceb710cf6a44e9617e43eebb6e3e43ad855a34da7b4b6a73537d8a6763", size = 1754175, upload-time = "2026-03-28T17:16:28.18Z" },
- { url = "https://files.pythonhosted.org/packages/f4/30/e717fc5df83133ba467a560b6d8ef20197037b4bb5d7075b90037de1018e/aiohttp-3.13.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4a31c0c587a8a038f19a4c7e60654a6c899c9de9174593a13e7cc6e15ff271f9", size = 1762049, upload-time = "2026-03-28T17:16:30.941Z" },
- { url = "https://files.pythonhosted.org/packages/e4/28/8f7a2d4492e336e40005151bdd94baf344880a4707573378579f833a64c1/aiohttp-3.13.4-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:2062f675f3fe6e06d6113eb74a157fb9df58953ffed0cdb4182554b116545758", size = 1570861, upload-time = "2026-03-28T17:16:32.953Z" },
- { url = "https://files.pythonhosted.org/packages/78/45/12e1a3d0645968b1c38de4b23fdf270b8637735ea057d4f84482ff918ad9/aiohttp-3.13.4-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:3d1ba8afb847ff80626d5e408c1fdc99f942acc877d0702fe137015903a220a9", size = 1790003, upload-time = "2026-03-28T17:16:35.468Z" },
- { url = "https://files.pythonhosted.org/packages/eb/0f/60374e18d590de16dcb39d6ff62f39c096c1b958e6f37727b5870026ea30/aiohttp-3.13.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b08149419994cdd4d5eecf7fd4bc5986b5a9380285bcd01ab4c0d6bfca47b79d", size = 1737289, upload-time = "2026-03-28T17:16:38.187Z" },
- { url = "https://files.pythonhosted.org/packages/02/bf/535e58d886cfbc40a8b0013c974afad24ef7632d645bca0b678b70033a60/aiohttp-3.13.4-cp312-cp312-win32.whl", hash = "sha256:fc432f6a2c4f720180959bc19aa37259651c1a4ed8af8afc84dd41c60f15f791", size = 434185, upload-time = "2026-03-28T17:16:40.735Z" },
- { url = "https://files.pythonhosted.org/packages/1e/1a/d92e3325134ebfff6f4069f270d3aac770d63320bd1fcd0eca023e74d9a8/aiohttp-3.13.4-cp312-cp312-win_amd64.whl", hash = "sha256:6148c9ae97a3e8bff9a1fc9c757fa164116f86c100468339730e717590a3fb77", size = 461285, upload-time = "2026-03-28T17:16:42.713Z" },
- { url = "https://files.pythonhosted.org/packages/e3/ac/892f4162df9b115b4758d615f32ec63d00f3084c705ff5526630887b9b42/aiohttp-3.13.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:63dd5e5b1e43b8fb1e91b79b7ceba1feba588b317d1edff385084fcc7a0a4538", size = 745744, upload-time = "2026-03-28T17:16:44.67Z" },
- { url = "https://files.pythonhosted.org/packages/97/a9/c5b87e4443a2f0ea88cb3000c93a8fdad1ee63bffc9ded8d8c8e0d66efc6/aiohttp-3.13.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:746ac3cc00b5baea424dacddea3ec2c2702f9590de27d837aa67004db1eebc6e", size = 498178, upload-time = "2026-03-28T17:16:46.766Z" },
- { url = "https://files.pythonhosted.org/packages/94/42/07e1b543a61250783650df13da8ddcdc0d0a5538b2bd15cef6e042aefc61/aiohttp-3.13.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bda8f16ea99d6a6705e5946732e48487a448be874e54a4f73d514660ff7c05d3", size = 498331, upload-time = "2026-03-28T17:16:48.9Z" },
- { url = "https://files.pythonhosted.org/packages/20/d6/492f46bf0328534124772d0cf58570acae5b286ea25006900650f69dae0e/aiohttp-3.13.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4b061e7b5f840391e3f64d0ddf672973e45c4cfff7a0feea425ea24e51530fc2", size = 1744414, upload-time = "2026-03-28T17:16:50.968Z" },
- { url = "https://files.pythonhosted.org/packages/e2/4d/e02627b2683f68051246215d2d62b2d2f249ff7a285e7a858dc47d6b6a14/aiohttp-3.13.4-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b252e8d5cd66184b570d0d010de742736e8a4fab22c58299772b0c5a466d4b21", size = 1719226, upload-time = "2026-03-28T17:16:53.173Z" },
- { url = "https://files.pythonhosted.org/packages/7b/6c/5d0a3394dd2b9f9aeba6e1b6065d0439e4b75d41f1fb09a3ec010b43552b/aiohttp-3.13.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:20af8aad61d1803ff11152a26146d8d81c266aa8c5aa9b4504432abb965c36a0", size = 1782110, upload-time = "2026-03-28T17:16:55.362Z" },
- { url = "https://files.pythonhosted.org/packages/0d/2d/c20791e3437700a7441a7edfb59731150322424f5aadf635602d1d326101/aiohttp-3.13.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:13a5cc924b59859ad2adb1478e31f410a7ed46e92a2a619d6d1dd1a63c1a855e", size = 1884809, upload-time = "2026-03-28T17:16:57.734Z" },
- { url = "https://files.pythonhosted.org/packages/c8/94/d99dbfbd1924a87ef643833932eb2a3d9e5eee87656efea7d78058539eff/aiohttp-3.13.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:534913dfb0a644d537aebb4123e7d466d94e3be5549205e6a31f72368980a81a", size = 1764938, upload-time = "2026-03-28T17:17:00.221Z" },
- { url = "https://files.pythonhosted.org/packages/49/61/3ce326a1538781deb89f6cf5e094e2029cd308ed1e21b2ba2278b08426f6/aiohttp-3.13.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:320e40192a2dcc1cf4b5576936e9652981ab596bf81eb309535db7e2f5b5672f", size = 1570697, upload-time = "2026-03-28T17:17:02.985Z" },
- { url = "https://files.pythonhosted.org/packages/b6/77/4ab5a546857bb3028fbaf34d6eea180267bdab022ee8b1168b1fcde4bfdd/aiohttp-3.13.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9e587fcfce2bcf06526a43cb705bdee21ac089096f2e271d75de9c339db3100c", size = 1702258, upload-time = "2026-03-28T17:17:05.28Z" },
- { url = "https://files.pythonhosted.org/packages/79/63/d8f29021e39bc5af8e5d5e9da1b07976fb9846487a784e11e4f4eeda4666/aiohttp-3.13.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:9eb9c2eea7278206b5c6c1441fdd9dc420c278ead3f3b2cc87f9b693698cc500", size = 1740287, upload-time = "2026-03-28T17:17:07.712Z" },
- { url = "https://files.pythonhosted.org/packages/55/3a/cbc6b3b124859a11bc8055d3682c26999b393531ef926754a3445b99dfef/aiohttp-3.13.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:29be00c51972b04bf9d5c8f2d7f7314f48f96070ca40a873a53056e652e805f7", size = 1753011, upload-time = "2026-03-28T17:17:10.053Z" },
- { url = "https://files.pythonhosted.org/packages/e0/30/836278675205d58c1368b21520eab9572457cf19afd23759216c04483048/aiohttp-3.13.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:90c06228a6c3a7c9f776fe4fc0b7ff647fffd3bed93779a6913c804ae00c1073", size = 1566359, upload-time = "2026-03-28T17:17:12.433Z" },
- { url = "https://files.pythonhosted.org/packages/50/b4/8032cc9b82d17e4277704ba30509eaccb39329dc18d6a35f05e424439e32/aiohttp-3.13.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:a533ec132f05fd9a1d959e7f34184cd7d5e8511584848dab85faefbaac573069", size = 1785537, upload-time = "2026-03-28T17:17:14.721Z" },
- { url = "https://files.pythonhosted.org/packages/17/7d/5873e98230bde59f493bf1f7c3e327486a4b5653fa401144704df5d00211/aiohttp-3.13.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1c946f10f413836f82ea4cfb90200d2a59578c549f00857e03111cf45ad01ca5", size = 1740752, upload-time = "2026-03-28T17:17:17.387Z" },
- { url = "https://files.pythonhosted.org/packages/7b/f2/13e46e0df051494d7d3c68b7f72d071f48c384c12716fc294f75d5b1a064/aiohttp-3.13.4-cp313-cp313-win32.whl", hash = "sha256:48708e2706106da6967eff5908c78ca3943f005ed6bcb75da2a7e4da94ef8c70", size = 433187, upload-time = "2026-03-28T17:17:19.523Z" },
- { url = "https://files.pythonhosted.org/packages/ea/c0/649856ee655a843c8f8664592cfccb73ac80ede6a8c8db33a25d810c12db/aiohttp-3.13.4-cp313-cp313-win_amd64.whl", hash = "sha256:74a2eb058da44fa3a877a49e2095b591d4913308bb424c418b77beb160c55ce3", size = 459778, upload-time = "2026-03-28T17:17:21.964Z" },
- { url = "https://files.pythonhosted.org/packages/6d/29/6657cc37ae04cacc2dbf53fb730a06b6091cc4cbe745028e047c53e6d840/aiohttp-3.13.4-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:e0a2c961fc92abeff61d6444f2ce6ad35bb982db9fc8ff8a47455beacf454a57", size = 749363, upload-time = "2026-03-28T17:17:24.044Z" },
- { url = "https://files.pythonhosted.org/packages/90/7f/30ccdf67ca3d24b610067dc63d64dcb91e5d88e27667811640644aa4a85d/aiohttp-3.13.4-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:153274535985a0ff2bff1fb6c104ed547cec898a09213d21b0f791a44b14d933", size = 499317, upload-time = "2026-03-28T17:17:26.199Z" },
- { url = "https://files.pythonhosted.org/packages/93/13/e372dd4e68ad04ee25dafb050c7f98b0d91ea643f7352757e87231102555/aiohttp-3.13.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:351f3171e2458da3d731ce83f9e6b9619e325c45cbd534c7759750cabf453ad7", size = 500477, upload-time = "2026-03-28T17:17:28.279Z" },
- { url = "https://files.pythonhosted.org/packages/e5/fe/ee6298e8e586096fb6f5eddd31393d8544f33ae0792c71ecbb4c2bef98ac/aiohttp-3.13.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f989ac8bc5595ff761a5ccd32bdb0768a117f36dd1504b1c2c074ed5d3f4df9c", size = 1737227, upload-time = "2026-03-28T17:17:30.587Z" },
- { url = "https://files.pythonhosted.org/packages/b0/b9/a7a0463a09e1a3fe35100f74324f23644bfc3383ac5fd5effe0722a5f0b7/aiohttp-3.13.4-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d36fc1709110ec1e87a229b201dd3ddc32aa01e98e7868083a794609b081c349", size = 1694036, upload-time = "2026-03-28T17:17:33.29Z" },
- { url = "https://files.pythonhosted.org/packages/57/7c/8972ae3fb7be00a91aee6b644b2a6a909aedb2c425269a3bfd90115e6f8f/aiohttp-3.13.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:42adaeea83cbdf069ab94f5103ce0787c21fb1a0153270da76b59d5578302329", size = 1786814, upload-time = "2026-03-28T17:17:36.035Z" },
- { url = "https://files.pythonhosted.org/packages/93/01/c81e97e85c774decbaf0d577de7d848934e8166a3a14ad9f8aa5be329d28/aiohttp-3.13.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:92deb95469928cc41fd4b42a95d8012fa6df93f6b1c0a83af0ffbc4a5e218cde", size = 1866676, upload-time = "2026-03-28T17:17:38.441Z" },
- { url = "https://files.pythonhosted.org/packages/5a/5f/5b46fe8694a639ddea2cd035bf5729e4677ea882cb251396637e2ef1590d/aiohttp-3.13.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0c0c7c07c4257ef3a1df355f840bc62d133bcdef5c1c5ba75add3c08553e2eed", size = 1740842, upload-time = "2026-03-28T17:17:40.783Z" },
- { url = "https://files.pythonhosted.org/packages/20/a2/0d4b03d011cca6b6b0acba8433193c1e484efa8d705ea58295590fe24203/aiohttp-3.13.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f062c45de8a1098cb137a1898819796a2491aec4e637a06b03f149315dff4d8f", size = 1566508, upload-time = "2026-03-28T17:17:43.235Z" },
- { url = "https://files.pythonhosted.org/packages/98/17/e689fd500da52488ec5f889effd6404dece6a59de301e380f3c64f167beb/aiohttp-3.13.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:76093107c531517001114f0ebdb4f46858ce818590363e3e99a4a2280334454a", size = 1700569, upload-time = "2026-03-28T17:17:46.165Z" },
- { url = "https://files.pythonhosted.org/packages/d8/0d/66402894dbcf470ef7db99449e436105ea862c24f7ea4c95c683e635af35/aiohttp-3.13.4-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:6f6ec32162d293b82f8b63a16edc80769662fbd5ae6fbd4936d3206a2c2cc63b", size = 1707407, upload-time = "2026-03-28T17:17:48.825Z" },
- { url = "https://files.pythonhosted.org/packages/2f/eb/af0ab1a3650092cbd8e14ef29e4ab0209e1460e1c299996c3f8288b3f1ff/aiohttp-3.13.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:5903e2db3d202a00ad9f0ec35a122c005e85d90c9836ab4cda628f01edf425e2", size = 1752214, upload-time = "2026-03-28T17:17:51.206Z" },
- { url = "https://files.pythonhosted.org/packages/5a/bf/72326f8a98e4c666f292f03c385545963cc65e358835d2a7375037a97b57/aiohttp-3.13.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:2d5bea57be7aca98dbbac8da046d99b5557c5cf4e28538c4c786313078aca09e", size = 1562162, upload-time = "2026-03-28T17:17:53.634Z" },
- { url = "https://files.pythonhosted.org/packages/67/9f/13b72435f99151dd9a5469c96b3b5f86aa29b7e785ca7f35cf5e538f74c0/aiohttp-3.13.4-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:bcf0c9902085976edc0232b75006ef38f89686901249ce14226b6877f88464fb", size = 1768904, upload-time = "2026-03-28T17:17:55.991Z" },
- { url = "https://files.pythonhosted.org/packages/18/bc/28d4970e7d5452ac7776cdb5431a1164a0d9cf8bd2fffd67b4fb463aa56d/aiohttp-3.13.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c3295f98bfeed2e867cab588f2a146a9db37a85e3ae9062abf46ba062bd29165", size = 1723378, upload-time = "2026-03-28T17:17:58.348Z" },
- { url = "https://files.pythonhosted.org/packages/53/74/b32458ca1a7f34d65bdee7aef2036adbe0438123d3d53e2b083c453c24dd/aiohttp-3.13.4-cp314-cp314-win32.whl", hash = "sha256:a598a5c5767e1369d8f5b08695cab1d8160040f796c4416af76fd773d229b3c9", size = 438711, upload-time = "2026-03-28T17:18:00.728Z" },
- { url = "https://files.pythonhosted.org/packages/40/b2/54b487316c2df3e03a8f3435e9636f8a81a42a69d942164830d193beb56a/aiohttp-3.13.4-cp314-cp314-win_amd64.whl", hash = "sha256:c555db4bc7a264bead5a7d63d92d41a1122fcd39cc62a4db815f45ad46f9c2c8", size = 464977, upload-time = "2026-03-28T17:18:03.367Z" },
- { url = "https://files.pythonhosted.org/packages/47/fb/e41b63c6ce71b07a59243bb8f3b457ee0c3402a619acb9d2c0d21ef0e647/aiohttp-3.13.4-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:45abbbf09a129825d13c18c7d3182fecd46d9da3cfc383756145394013604ac1", size = 781549, upload-time = "2026-03-28T17:18:05.779Z" },
- { url = "https://files.pythonhosted.org/packages/97/53/532b8d28df1e17e44c4d9a9368b78dcb6bf0b51037522136eced13afa9e8/aiohttp-3.13.4-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:74c80b2bc2c2adb7b3d1941b2b60701ee2af8296fc8aad8b8bc48bc25767266c", size = 514383, upload-time = "2026-03-28T17:18:08.096Z" },
- { url = "https://files.pythonhosted.org/packages/1b/1f/62e5d400603e8468cd635812d99cb81cfdc08127a3dc474c647615f31339/aiohttp-3.13.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c97989ae40a9746650fa196894f317dafc12227c808c774929dda0ff873a5954", size = 518304, upload-time = "2026-03-28T17:18:10.642Z" },
- { url = "https://files.pythonhosted.org/packages/90/57/2326b37b10896447e3c6e0cbef4fe2486d30913639a5cfd1332b5d870f82/aiohttp-3.13.4-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dae86be9811493f9990ef44fff1685f5c1a3192e9061a71a109d527944eed551", size = 1893433, upload-time = "2026-03-28T17:18:13.121Z" },
- { url = "https://files.pythonhosted.org/packages/d2/b4/a24d82112c304afdb650167ef2fe190957d81cbddac7460bedd245f765aa/aiohttp-3.13.4-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:1db491abe852ca2fa6cc48a3341985b0174b3741838e1341b82ac82c8bd9e871", size = 1755901, upload-time = "2026-03-28T17:18:16.21Z" },
- { url = "https://files.pythonhosted.org/packages/9e/2d/0883ef9d878d7846287f036c162a951968f22aabeef3ac97b0bea6f76d5d/aiohttp-3.13.4-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0e5d701c0aad02a7dce72eef6b93226cf3734330f1a31d69ebbf69f33b86666e", size = 1876093, upload-time = "2026-03-28T17:18:18.703Z" },
- { url = "https://files.pythonhosted.org/packages/ad/52/9204bb59c014869b71971addad6778f005daa72a96eed652c496789d7468/aiohttp-3.13.4-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8ac32a189081ae0a10ba18993f10f338ec94341f0d5df8fff348043962f3c6f8", size = 1970815, upload-time = "2026-03-28T17:18:21.858Z" },
- { url = "https://files.pythonhosted.org/packages/d6/b5/e4eb20275a866dde0f570f411b36c6b48f7b53edfe4f4071aa1b0728098a/aiohttp-3.13.4-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:98e968cdaba43e45c73c3f306fca418c8009a957733bac85937c9f9cf3f4de27", size = 1816223, upload-time = "2026-03-28T17:18:24.729Z" },
- { url = "https://files.pythonhosted.org/packages/d8/23/e98075c5bb146aa61a1239ee1ac7714c85e814838d6cebbe37d3fe19214a/aiohttp-3.13.4-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ca114790c9144c335d538852612d3e43ea0f075288f4849cf4b05d6cd2238ce7", size = 1649145, upload-time = "2026-03-28T17:18:27.269Z" },
- { url = "https://files.pythonhosted.org/packages/d6/c1/7bad8be33bb06c2bb224b6468874346026092762cbec388c3bdb65a368ee/aiohttp-3.13.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:ea2e071661ba9cfe11eabbc81ac5376eaeb3061f6e72ec4cc86d7cdd1ffbdbbb", size = 1816562, upload-time = "2026-03-28T17:18:29.847Z" },
- { url = "https://files.pythonhosted.org/packages/5c/10/c00323348695e9a5e316825969c88463dcc24c7e9d443244b8a2c9cf2eae/aiohttp-3.13.4-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:34e89912b6c20e0fd80e07fa401fd218a410aa1ce9f1c2f1dad6db1bd0ce0927", size = 1800333, upload-time = "2026-03-28T17:18:32.269Z" },
- { url = "https://files.pythonhosted.org/packages/84/43/9b2147a1df3559f49bd723e22905b46a46c068a53adb54abdca32c4de180/aiohttp-3.13.4-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:0e217cf9f6a42908c52b46e42c568bd57adc39c9286ced31aaace614b6087965", size = 1820617, upload-time = "2026-03-28T17:18:35.238Z" },
- { url = "https://files.pythonhosted.org/packages/a9/7f/b3481a81e7a586d02e99387b18c6dafff41285f6efd3daa2124c01f87eae/aiohttp-3.13.4-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:0c296f1221e21ba979f5ac1964c3b78cfde15c5c5f855ffd2caab337e9cd9182", size = 1643417, upload-time = "2026-03-28T17:18:37.949Z" },
- { url = "https://files.pythonhosted.org/packages/8f/72/07181226bc99ce1124e0f89280f5221a82d3ae6a6d9d1973ce429d48e52b/aiohttp-3.13.4-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:d99a9d168ebaffb74f36d011750e490085ac418f4db926cce3989c8fe6cb6b1b", size = 1849286, upload-time = "2026-03-28T17:18:40.534Z" },
- { url = "https://files.pythonhosted.org/packages/1a/e6/1b3566e103eca6da5be4ae6713e112a053725c584e96574caf117568ffef/aiohttp-3.13.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:cb19177205d93b881f3f89e6081593676043a6828f59c78c17a0fd6c1fbed2ba", size = 1782635, upload-time = "2026-03-28T17:18:43.073Z" },
- { url = "https://files.pythonhosted.org/packages/37/58/1b11c71904b8d079eb0c39fe664180dd1e14bebe5608e235d8bfbadc8929/aiohttp-3.13.4-cp314-cp314t-win32.whl", hash = "sha256:c606aa5656dab6552e52ca368e43869c916338346bfaf6304e15c58fb113ea30", size = 472537, upload-time = "2026-03-28T17:18:46.286Z" },
- { url = "https://files.pythonhosted.org/packages/bc/8f/87c56a1a1977d7dddea5b31e12189665a140fdb48a71e9038ff90bb564ec/aiohttp-3.13.4-cp314-cp314t-win_amd64.whl", hash = "sha256:014dcc10ec8ab8db681f0d68e939d1e9286a5aa2b993cbbdb0db130853e02144", size = 506381, upload-time = "2026-03-28T17:18:48.74Z" },
+sdist = { url = "https://files.pythonhosted.org/packages/77/9a/152096d4808df8e4268befa55fba462f440f14beab85e8ad9bf990516918/aiohttp-3.13.5.tar.gz", hash = "sha256:9d98cc980ecc96be6eb4c1994ce35d28d8b1f5e5208a23b421187d1209dbb7d1", size = 7858271, upload-time = "2026-03-31T22:01:03.343Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/d6/f5/a20c4ac64aeaef1679e25c9983573618ff765d7aa829fa2b84ae7573169e/aiohttp-3.13.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7ab7229b6f9b5c1ba4910d6c41a9eb11f543eadb3f384df1b4c293f4e73d44d6", size = 757513, upload-time = "2026-03-31T21:57:02.146Z" },
+ { url = "https://files.pythonhosted.org/packages/75/0a/39fa6c6b179b53fcb3e4b3d2b6d6cad0180854eda17060c7218540102bef/aiohttp-3.13.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8f14c50708bb156b3a3ca7230b3d820199d56a48e3af76fa21c2d6087190fe3d", size = 506748, upload-time = "2026-03-31T21:57:04.275Z" },
+ { url = "https://files.pythonhosted.org/packages/87/ec/e38ce072e724fd7add6243613f8d1810da084f54175353d25ccf9f9c7e5a/aiohttp-3.13.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e7d2f8616f0ff60bd332022279011776c3ac0faa0f1b463f7bb12326fbc97a1c", size = 501673, upload-time = "2026-03-31T21:57:06.208Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/ba/3bc7525d7e2beaa11b309a70d48b0d3cfc3c2089ec6a7d0820d59c657053/aiohttp-3.13.5-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a2567b72e1ffc3ab25510db43f355b29eeada56c0a622e58dcdb19530eb0a3cb", size = 1763757, upload-time = "2026-03-31T21:57:07.882Z" },
+ { url = "https://files.pythonhosted.org/packages/5e/ab/e87744cf18f1bd78263aba24924d4953b41086bd3a31d22452378e9028a0/aiohttp-3.13.5-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:fb0540c854ac9c0c5ad495908fdfd3e332d553ec731698c0e29b1877ba0d2ec6", size = 1720152, upload-time = "2026-03-31T21:57:09.946Z" },
+ { url = "https://files.pythonhosted.org/packages/6b/f3/ed17a6f2d742af17b50bae2d152315ed1b164b07a5fd5cc1754d99e4dfa5/aiohttp-3.13.5-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c9883051c6972f58bfc4ebb2116345ee2aa151178e99c3f2b2bbe2af712abd13", size = 1818010, upload-time = "2026-03-31T21:57:12.157Z" },
+ { url = "https://files.pythonhosted.org/packages/53/06/ecbc63dc937192e2a5cb46df4d3edb21deb8225535818802f210a6ea5816/aiohttp-3.13.5-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2294172ce08a82fb7c7273485895de1fa1186cc8294cfeb6aef4af42ad261174", size = 1907251, upload-time = "2026-03-31T21:57:14.023Z" },
+ { url = "https://files.pythonhosted.org/packages/7e/a5/0521aa32c1ddf3aa1e71dcc466be0b7db2771907a13f18cddaa45967d97b/aiohttp-3.13.5-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3a807cabd5115fb55af198b98178997a5e0e57dead43eb74a93d9c07d6d4a7dc", size = 1759969, upload-time = "2026-03-31T21:57:16.146Z" },
+ { url = "https://files.pythonhosted.org/packages/f6/78/a38f8c9105199dd3b9706745865a8a59d0041b6be0ca0cc4b2ccf1bab374/aiohttp-3.13.5-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:aa6d0d932e0f39c02b80744273cd5c388a2d9bc07760a03164f229c8e02662f6", size = 1616871, upload-time = "2026-03-31T21:57:17.856Z" },
+ { url = "https://files.pythonhosted.org/packages/6f/41/27392a61ead8ab38072105c71aa44ff891e71653fe53d576a7067da2b4e8/aiohttp-3.13.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:60869c7ac4aaabe7110f26499f3e6e5696eae98144735b12a9c3d9eae2b51a49", size = 1739844, upload-time = "2026-03-31T21:57:19.679Z" },
+ { url = "https://files.pythonhosted.org/packages/6e/55/5564e7ae26d94f3214250009a0b1c65a0c6af4bf88924ccb6fdab901de28/aiohttp-3.13.5-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:26d2f8546f1dfa75efa50c3488215a903c0168d253b75fba4210f57ab77a0fb8", size = 1731969, upload-time = "2026-03-31T21:57:22.006Z" },
+ { url = "https://files.pythonhosted.org/packages/6d/c5/705a3929149865fc941bcbdd1047b238e4a72bcb215a9b16b9d7a2e8d992/aiohttp-3.13.5-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f1162a1492032c82f14271e831c8f4b49f2b6078f4f5fc74de2c912fa225d51d", size = 1795193, upload-time = "2026-03-31T21:57:24.256Z" },
+ { url = "https://files.pythonhosted.org/packages/a6/19/edabed62f718d02cff7231ca0db4ef1c72504235bc467f7b67adb1679f48/aiohttp-3.13.5-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:8b14eb3262fad0dc2f89c1a43b13727e709504972186ff6a99a3ecaa77102b6c", size = 1606477, upload-time = "2026-03-31T21:57:26.364Z" },
+ { url = "https://files.pythonhosted.org/packages/de/fc/76f80ef008675637d88d0b21584596dc27410a990b0918cb1e5776545b5b/aiohttp-3.13.5-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:ca9ac61ac6db4eb6c2a0cd1d0f7e1357647b638ccc92f7e9d8d133e71ed3c6ac", size = 1813198, upload-time = "2026-03-31T21:57:28.316Z" },
+ { url = "https://files.pythonhosted.org/packages/e5/67/5b3ac26b80adb20ea541c487f73730dc8fa107d632c998f25bbbab98fcda/aiohttp-3.13.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7996023b2ed59489ae4762256c8516df9820f751cf2c5da8ed2fb20ee50abab3", size = 1752321, upload-time = "2026-03-31T21:57:30.549Z" },
+ { url = "https://files.pythonhosted.org/packages/88/06/e4a2e49255ea23fa4feeb5ab092d90240d927c15e47b5b5c48dff5a9ce29/aiohttp-3.13.5-cp311-cp311-win32.whl", hash = "sha256:77dfa48c9f8013271011e51c00f8ada19851f013cde2c48fca1ba5e0caf5bb06", size = 439069, upload-time = "2026-03-31T21:57:32.388Z" },
+ { url = "https://files.pythonhosted.org/packages/c0/43/8c7163a596dab4f8be12c190cf467a1e07e4734cf90eebb39f7f5d53fc6a/aiohttp-3.13.5-cp311-cp311-win_amd64.whl", hash = "sha256:d3a4834f221061624b8887090637db9ad4f61752001eae37d56c52fddade2dc8", size = 462859, upload-time = "2026-03-31T21:57:34.455Z" },
+ { url = "https://files.pythonhosted.org/packages/be/6f/353954c29e7dcce7cf00280a02c75f30e133c00793c7a2ed3776d7b2f426/aiohttp-3.13.5-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:023ecba036ddd840b0b19bf195bfae970083fd7024ce1ac22e9bba90464620e9", size = 748876, upload-time = "2026-03-31T21:57:36.319Z" },
+ { url = "https://files.pythonhosted.org/packages/f5/1b/428a7c64687b3b2e9cd293186695affc0e1e54a445d0361743b231f11066/aiohttp-3.13.5-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:15c933ad7920b7d9a20de151efcd05a6e38302cbf0e10c9b2acb9a42210a2416", size = 499557, upload-time = "2026-03-31T21:57:38.236Z" },
+ { url = "https://files.pythonhosted.org/packages/29/47/7be41556bfbb6917069d6a6634bb7dd5e163ba445b783a90d40f5ac7e3a7/aiohttp-3.13.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ab2899f9fa2f9f741896ebb6fa07c4c883bfa5c7f2ddd8cf2aafa86fa981b2d2", size = 500258, upload-time = "2026-03-31T21:57:39.923Z" },
+ { url = "https://files.pythonhosted.org/packages/67/84/c9ecc5828cb0b3695856c07c0a6817a99d51e2473400f705275a2b3d9239/aiohttp-3.13.5-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a60eaa2d440cd4707696b52e40ed3e2b0f73f65be07fd0ef23b6b539c9c0b0b4", size = 1749199, upload-time = "2026-03-31T21:57:41.938Z" },
+ { url = "https://files.pythonhosted.org/packages/f0/d3/3c6d610e66b495657622edb6ae7c7fd31b2e9086b4ec50b47897ad6042a9/aiohttp-3.13.5-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:55b3bdd3292283295774ab585160c4004f4f2f203946997f49aac032c84649e9", size = 1721013, upload-time = "2026-03-31T21:57:43.904Z" },
+ { url = "https://files.pythonhosted.org/packages/49/a0/24409c12217456df0bae7babe3b014e460b0b38a8e60753d6cb339f6556d/aiohttp-3.13.5-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c2b2355dc094e5f7d45a7bb262fe7207aa0460b37a0d87027dcf21b5d890e7d5", size = 1781501, upload-time = "2026-03-31T21:57:46.285Z" },
+ { url = "https://files.pythonhosted.org/packages/98/9d/b65ec649adc5bccc008b0957a9a9c691070aeac4e41cea18559fef49958b/aiohttp-3.13.5-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b38765950832f7d728297689ad78f5f2cf79ff82487131c4d26fe6ceecdc5f8e", size = 1878981, upload-time = "2026-03-31T21:57:48.734Z" },
+ { url = "https://files.pythonhosted.org/packages/57/d8/8d44036d7eb7b6a8ec4c5494ea0c8c8b94fbc0ed3991c1a7adf230df03bf/aiohttp-3.13.5-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b18f31b80d5a33661e08c89e202edabf1986e9b49c42b4504371daeaa11b47c1", size = 1767934, upload-time = "2026-03-31T21:57:51.171Z" },
+ { url = "https://files.pythonhosted.org/packages/31/04/d3f8211f273356f158e3464e9e45484d3fb8c4ce5eb2f6fe9405c3273983/aiohttp-3.13.5-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:33add2463dde55c4f2d9635c6ab33ce154e5ecf322bd26d09af95c5f81cfa286", size = 1566671, upload-time = "2026-03-31T21:57:53.326Z" },
+ { url = "https://files.pythonhosted.org/packages/41/db/073e4ebe00b78e2dfcacff734291651729a62953b48933d765dc513bf798/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:327cc432fdf1356fb4fbc6fe833ad4e9f6aacb71a8acaa5f1855e4b25910e4a9", size = 1705219, upload-time = "2026-03-31T21:57:55.385Z" },
+ { url = "https://files.pythonhosted.org/packages/48/45/7dfba71a2f9fd97b15c95c06819de7eb38113d2cdb6319669195a7d64270/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:7c35b0bf0b48a70b4cb4fc5d7bed9b932532728e124874355de1a0af8ec4bc88", size = 1743049, upload-time = "2026-03-31T21:57:57.341Z" },
+ { url = "https://files.pythonhosted.org/packages/18/71/901db0061e0f717d226386a7f471bb59b19566f2cae5f0d93874b017271f/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:df23d57718f24badef8656c49743e11a89fd6f5358fa8a7b96e728fda2abf7d3", size = 1749557, upload-time = "2026-03-31T21:57:59.626Z" },
+ { url = "https://files.pythonhosted.org/packages/08/d5/41eebd16066e59cd43728fe74bce953d7402f2b4ddfdfef2c0e9f17ca274/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:02e048037a6501a5ec1f6fc9736135aec6eb8a004ce48838cb951c515f32c80b", size = 1558931, upload-time = "2026-03-31T21:58:01.972Z" },
+ { url = "https://files.pythonhosted.org/packages/30/e6/4a799798bf05740e66c3a1161079bda7a3dd8e22ca392481d7a7f9af82a6/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:31cebae8b26f8a615d2b546fee45d5ffb76852ae6450e2a03f42c9102260d6fe", size = 1774125, upload-time = "2026-03-31T21:58:04.007Z" },
+ { url = "https://files.pythonhosted.org/packages/84/63/7749337c90f92bc2cb18f9560d67aa6258c7060d1397d21529b8004fcf6f/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:888e78eb5ca55a615d285c3c09a7a91b42e9dd6fc699b166ebd5dee87c9ccf14", size = 1732427, upload-time = "2026-03-31T21:58:06.337Z" },
+ { url = "https://files.pythonhosted.org/packages/98/de/cf2f44ff98d307e72fb97d5f5bbae3bfcb442f0ea9790c0bf5c5c2331404/aiohttp-3.13.5-cp312-cp312-win32.whl", hash = "sha256:8bd3ec6376e68a41f9f95f5ed170e2fcf22d4eb27a1f8cb361d0508f6e0557f3", size = 433534, upload-time = "2026-03-31T21:58:08.712Z" },
+ { url = "https://files.pythonhosted.org/packages/aa/ca/eadf6f9c8fa5e31d40993e3db153fb5ed0b11008ad5d9de98a95045bed84/aiohttp-3.13.5-cp312-cp312-win_amd64.whl", hash = "sha256:110e448e02c729bcebb18c60b9214a87ba33bac4a9fa5e9a5f139938b56c6cb1", size = 460446, upload-time = "2026-03-31T21:58:10.945Z" },
+ { url = "https://files.pythonhosted.org/packages/78/e9/d76bf503005709e390122d34e15256b88f7008e246c4bdbe915cd4f1adce/aiohttp-3.13.5-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a5029cc80718bbd545123cd8fe5d15025eccaaaace5d0eeec6bd556ad6163d61", size = 742930, upload-time = "2026-03-31T21:58:13.155Z" },
+ { url = "https://files.pythonhosted.org/packages/57/00/4b7b70223deaebd9bb85984d01a764b0d7bd6526fcdc73cca83bcbe7243e/aiohttp-3.13.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4bb6bf5811620003614076bdc807ef3b5e38244f9d25ca5fe888eaccea2a9832", size = 496927, upload-time = "2026-03-31T21:58:15.073Z" },
+ { url = "https://files.pythonhosted.org/packages/9c/f5/0fb20fb49f8efdcdce6cd8127604ad2c503e754a8f139f5e02b01626523f/aiohttp-3.13.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a84792f8631bf5a94e52d9cc881c0b824ab42717165a5579c760b830d9392ac9", size = 497141, upload-time = "2026-03-31T21:58:17.009Z" },
+ { url = "https://files.pythonhosted.org/packages/3b/86/b7c870053e36a94e8951b803cb5b909bfbc9b90ca941527f5fcafbf6b0fa/aiohttp-3.13.5-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:57653eac22c6a4c13eb22ecf4d673d64a12f266e72785ab1c8b8e5940d0e8090", size = 1732476, upload-time = "2026-03-31T21:58:18.925Z" },
+ { url = "https://files.pythonhosted.org/packages/b5/e5/4e161f84f98d80c03a238671b4136e6530453d65262867d989bbe78244d0/aiohttp-3.13.5-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e5e5f7debc7a57af53fdf5c5009f9391d9f4c12867049d509bf7bb164a6e295b", size = 1706507, upload-time = "2026-03-31T21:58:21.094Z" },
+ { url = "https://files.pythonhosted.org/packages/d4/56/ea11a9f01518bd5a2a2fcee869d248c4b8a0cfa0bb13401574fa31adf4d4/aiohttp-3.13.5-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c719f65bebcdf6716f10e9eff80d27567f7892d8988c06de12bbbd39307c6e3a", size = 1773465, upload-time = "2026-03-31T21:58:23.159Z" },
+ { url = "https://files.pythonhosted.org/packages/eb/40/333ca27fb74b0383f17c90570c748f7582501507307350a79d9f9f3c6eb1/aiohttp-3.13.5-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d97f93fdae594d886c5a866636397e2bcab146fd7a132fd6bb9ce182224452f8", size = 1873523, upload-time = "2026-03-31T21:58:25.59Z" },
+ { url = "https://files.pythonhosted.org/packages/f0/d2/e2f77eef1acb7111405433c707dc735e63f67a56e176e72e9e7a2cd3f493/aiohttp-3.13.5-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3df334e39d4c2f899a914f1dba283c1aadc311790733f705182998c6f7cae665", size = 1754113, upload-time = "2026-03-31T21:58:27.624Z" },
+ { url = "https://files.pythonhosted.org/packages/fb/56/3f653d7f53c89669301ec9e42c95233e2a0c0a6dd051269e6e678db4fdb0/aiohttp-3.13.5-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fe6970addfea9e5e081401bcbadf865d2b6da045472f58af08427e108d618540", size = 1562351, upload-time = "2026-03-31T21:58:29.918Z" },
+ { url = "https://files.pythonhosted.org/packages/ec/a6/9b3e91eb8ae791cce4ee736da02211c85c6f835f1bdfac0594a8a3b7018c/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7becdf835feff2f4f335d7477f121af787e3504b48b449ff737afb35869ba7bb", size = 1693205, upload-time = "2026-03-31T21:58:32.214Z" },
+ { url = "https://files.pythonhosted.org/packages/98/fc/bfb437a99a2fcebd6b6eaec609571954de2ed424f01c352f4b5504371dd3/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:676e5651705ad5d8a70aeb8eb6936c436d8ebbd56e63436cb7dd9bb36d2a9a46", size = 1730618, upload-time = "2026-03-31T21:58:34.728Z" },
+ { url = "https://files.pythonhosted.org/packages/e4/b6/c8534862126191a034f68153194c389addc285a0f1347d85096d349bbc15/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:9b16c653d38eb1a611cc898c41e76859ca27f119d25b53c12875fd0474ae31a8", size = 1745185, upload-time = "2026-03-31T21:58:36.909Z" },
+ { url = "https://files.pythonhosted.org/packages/0b/93/4ca8ee2ef5236e2707e0fd5fecb10ce214aee1ff4ab307af9c558bda3b37/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:999802d5fa0389f58decd24b537c54aa63c01c3219ce17d1214cbda3c2b22d2d", size = 1557311, upload-time = "2026-03-31T21:58:39.38Z" },
+ { url = "https://files.pythonhosted.org/packages/57/ae/76177b15f18c5f5d094f19901d284025db28eccc5ae374d1d254181d33f4/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:ec707059ee75732b1ba130ed5f9580fe10ff75180c812bc267ded039db5128c6", size = 1773147, upload-time = "2026-03-31T21:58:41.476Z" },
+ { url = "https://files.pythonhosted.org/packages/01/a4/62f05a0a98d88af59d93b7fcac564e5f18f513cb7471696ac286db970d6a/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2d6d44a5b48132053c2f6cd5c8cb14bc67e99a63594e336b0f2af81e94d5530c", size = 1730356, upload-time = "2026-03-31T21:58:44.049Z" },
+ { url = "https://files.pythonhosted.org/packages/e4/85/fc8601f59dfa8c9523808281f2da571f8b4699685f9809a228adcc90838d/aiohttp-3.13.5-cp313-cp313-win32.whl", hash = "sha256:329f292ed14d38a6c4c435e465f48bebb47479fd676a0411936cc371643225cc", size = 432637, upload-time = "2026-03-31T21:58:46.167Z" },
+ { url = "https://files.pythonhosted.org/packages/c0/1b/ac685a8882896acf0f6b31d689e3792199cfe7aba37969fa91da63a7fa27/aiohttp-3.13.5-cp313-cp313-win_amd64.whl", hash = "sha256:69f571de7500e0557801c0b51f4780482c0ec5fe2ac851af5a92cfce1af1cb83", size = 458896, upload-time = "2026-03-31T21:58:48.119Z" },
+ { url = "https://files.pythonhosted.org/packages/5d/ce/46572759afc859e867a5bc8ec3487315869013f59281ce61764f76d879de/aiohttp-3.13.5-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:eb4639f32fd4a9904ab8fb45bf3383ba71137f3d9d4ba25b3b3f3109977c5b8c", size = 745721, upload-time = "2026-03-31T21:58:50.229Z" },
+ { url = "https://files.pythonhosted.org/packages/13/fe/8a2efd7626dbe6049b2ef8ace18ffda8a4dfcbe1bcff3ac30c0c7575c20b/aiohttp-3.13.5-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:7e5dc4311bd5ac493886c63cbf76ab579dbe4641268e7c74e48e774c74b6f2be", size = 497663, upload-time = "2026-03-31T21:58:52.232Z" },
+ { url = "https://files.pythonhosted.org/packages/9b/91/cc8cc78a111826c54743d88651e1687008133c37e5ee615fee9b57990fac/aiohttp-3.13.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:756c3c304d394977519824449600adaf2be0ccee76d206ee339c5e76b70ded25", size = 499094, upload-time = "2026-03-31T21:58:54.566Z" },
+ { url = "https://files.pythonhosted.org/packages/0a/33/a8362cb15cf16a3af7e86ed11962d5cd7d59b449202dc576cdc731310bde/aiohttp-3.13.5-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ecc26751323224cf8186efcf7fbcbc30f4e1d8c7970659daf25ad995e4032a56", size = 1726701, upload-time = "2026-03-31T21:58:56.864Z" },
+ { url = "https://files.pythonhosted.org/packages/45/0c/c091ac5c3a17114bd76cbf85d674650969ddf93387876cf67f754204bd77/aiohttp-3.13.5-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:10a75acfcf794edf9d8db50e5a7ec5fc818b2a8d3f591ce93bc7b1210df016d2", size = 1683360, upload-time = "2026-03-31T21:58:59.072Z" },
+ { url = "https://files.pythonhosted.org/packages/23/73/bcee1c2b79bc275e964d1446c55c54441a461938e70267c86afaae6fba27/aiohttp-3.13.5-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0f7a18f258d124cd678c5fe072fe4432a4d5232b0657fca7c1847f599233c83a", size = 1773023, upload-time = "2026-03-31T21:59:01.776Z" },
+ { url = "https://files.pythonhosted.org/packages/c7/ef/720e639df03004fee2d869f771799d8c23046dec47d5b81e396c7cda583a/aiohttp-3.13.5-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:df6104c009713d3a89621096f3e3e88cc323fd269dbd7c20afe18535094320be", size = 1853795, upload-time = "2026-03-31T21:59:04.568Z" },
+ { url = "https://files.pythonhosted.org/packages/bd/c9/989f4034fb46841208de7aeeac2c6d8300745ab4f28c42f629ba77c2d916/aiohttp-3.13.5-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:241a94f7de7c0c3b616627aaad530fe2cb620084a8b144d3be7b6ecfe95bae3b", size = 1730405, upload-time = "2026-03-31T21:59:07.221Z" },
+ { url = "https://files.pythonhosted.org/packages/ce/75/ee1fd286ca7dc599d824b5651dad7b3be7ff8d9a7e7b3fe9820d9180f7db/aiohttp-3.13.5-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c974fb66180e58709b6fc402846f13791240d180b74de81d23913abe48e96d94", size = 1558082, upload-time = "2026-03-31T21:59:09.484Z" },
+ { url = "https://files.pythonhosted.org/packages/c3/20/1e9e6650dfc436340116b7aa89ff8cb2bbdf0abc11dfaceaad8f74273a10/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:6e27ea05d184afac78aabbac667450c75e54e35f62238d44463131bd3f96753d", size = 1692346, upload-time = "2026-03-31T21:59:12.068Z" },
+ { url = "https://files.pythonhosted.org/packages/d8/40/8ebc6658d48ea630ac7903912fe0dd4e262f0e16825aa4c833c56c9f1f56/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:a79a6d399cef33a11b6f004c67bb07741d91f2be01b8d712d52c75711b1e07c7", size = 1698891, upload-time = "2026-03-31T21:59:14.552Z" },
+ { url = "https://files.pythonhosted.org/packages/d8/78/ea0ae5ec8ba7a5c10bdd6e318f1ba5e76fcde17db8275188772afc7917a4/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:c632ce9c0b534fbe25b52c974515ed674937c5b99f549a92127c85f771a78772", size = 1742113, upload-time = "2026-03-31T21:59:17.068Z" },
+ { url = "https://files.pythonhosted.org/packages/8a/66/9d308ed71e3f2491be1acb8769d96c6f0c47d92099f3bc9119cada27b357/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:fceedde51fbd67ee2bcc8c0b33d0126cc8b51ef3bbde2f86662bd6d5a6f10ec5", size = 1553088, upload-time = "2026-03-31T21:59:19.541Z" },
+ { url = "https://files.pythonhosted.org/packages/da/a6/6cc25ed8dfc6e00c90f5c6d126a98e2cf28957ad06fa1036bd34b6f24a2c/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:f92995dfec9420bb69ae629abf422e516923ba79ba4403bc750d94fb4a6c68c1", size = 1757976, upload-time = "2026-03-31T21:59:22.311Z" },
+ { url = "https://files.pythonhosted.org/packages/c1/2b/cce5b0ffe0de99c83e5e36d8f828e4161e415660a9f3e58339d07cce3006/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:20ae0ff08b1f2c8788d6fb85afcb798654ae6ba0b747575f8562de738078457b", size = 1712444, upload-time = "2026-03-31T21:59:24.635Z" },
+ { url = "https://files.pythonhosted.org/packages/6c/cf/9e1795b4160c58d29421eafd1a69c6ce351e2f7c8d3c6b7e4ca44aea1a5b/aiohttp-3.13.5-cp314-cp314-win32.whl", hash = "sha256:b20df693de16f42b2472a9c485e1c948ee55524786a0a34345511afdd22246f3", size = 438128, upload-time = "2026-03-31T21:59:27.291Z" },
+ { url = "https://files.pythonhosted.org/packages/22/4d/eaedff67fc805aeba4ba746aec891b4b24cebb1a7d078084b6300f79d063/aiohttp-3.13.5-cp314-cp314-win_amd64.whl", hash = "sha256:f85c6f327bf0b8c29da7d93b1cabb6363fb5e4e160a32fa241ed2dce21b73162", size = 464029, upload-time = "2026-03-31T21:59:29.429Z" },
+ { url = "https://files.pythonhosted.org/packages/79/11/c27d9332ee20d68dd164dc12a6ecdef2e2e35ecc97ed6cf0d2442844624b/aiohttp-3.13.5-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:1efb06900858bb618ff5cee184ae2de5828896c448403d51fb633f09e109be0a", size = 778758, upload-time = "2026-03-31T21:59:31.547Z" },
+ { url = "https://files.pythonhosted.org/packages/04/fb/377aead2e0a3ba5f09b7624f702a964bdf4f08b5b6728a9799830c80041e/aiohttp-3.13.5-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:fee86b7c4bd29bdaf0d53d14739b08a106fdda809ca5fe032a15f52fae5fe254", size = 512883, upload-time = "2026-03-31T21:59:34.098Z" },
+ { url = "https://files.pythonhosted.org/packages/bb/a6/aa109a33671f7a5d3bd78b46da9d852797c5e665bfda7d6b373f56bff2ec/aiohttp-3.13.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:20058e23909b9e65f9da62b396b77dfa95965cbe840f8def6e572538b1d32e36", size = 516668, upload-time = "2026-03-31T21:59:36.497Z" },
+ { url = "https://files.pythonhosted.org/packages/79/b3/ca078f9f2fa9563c36fb8ef89053ea2bb146d6f792c5104574d49d8acb63/aiohttp-3.13.5-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8cf20a8d6868cb15a73cab329ffc07291ba8c22b1b88176026106ae39aa6df0f", size = 1883461, upload-time = "2026-03-31T21:59:38.723Z" },
+ { url = "https://files.pythonhosted.org/packages/b7/e3/a7ad633ca1ca497b852233a3cce6906a56c3225fb6d9217b5e5e60b7419d/aiohttp-3.13.5-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:330f5da04c987f1d5bdb8ae189137c77139f36bd1cb23779ca1a354a4b027800", size = 1747661, upload-time = "2026-03-31T21:59:41.187Z" },
+ { url = "https://files.pythonhosted.org/packages/33/b9/cd6fe579bed34a906d3d783fe60f2fa297ef55b27bb4538438ee49d4dc41/aiohttp-3.13.5-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6f1cbf0c7926d315c3c26c2da41fd2b5d2fe01ac0e157b78caefc51a782196cf", size = 1863800, upload-time = "2026-03-31T21:59:43.84Z" },
+ { url = "https://files.pythonhosted.org/packages/c0/3f/2c1e2f5144cefa889c8afd5cf431994c32f3b29da9961698ff4e3811b79a/aiohttp-3.13.5-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:53fc049ed6390d05423ba33103ded7281fe897cf97878f369a527070bd95795b", size = 1958382, upload-time = "2026-03-31T21:59:46.187Z" },
+ { url = "https://files.pythonhosted.org/packages/66/1d/f31ec3f1013723b3babe3609e7f119c2c2fb6ef33da90061a705ef3e1bc8/aiohttp-3.13.5-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:898703aa2667e3c5ca4c54ca36cd73f58b7a38ef87a5606414799ebce4d3fd3a", size = 1803724, upload-time = "2026-03-31T21:59:48.656Z" },
+ { url = "https://files.pythonhosted.org/packages/0e/b4/57712dfc6f1542f067daa81eb61da282fab3e6f1966fca25db06c4fc62d5/aiohttp-3.13.5-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0494a01ca9584eea1e5fbd6d748e61ecff218c51b576ee1999c23db7066417d8", size = 1640027, upload-time = "2026-03-31T21:59:51.284Z" },
+ { url = "https://files.pythonhosted.org/packages/25/3c/734c878fb43ec083d8e31bf029daae1beafeae582d1b35da234739e82ee7/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:6cf81fe010b8c17b09495cbd15c1d35afbc8fb405c0c9cf4738e5ae3af1d65be", size = 1806644, upload-time = "2026-03-31T21:59:53.753Z" },
+ { url = "https://files.pythonhosted.org/packages/20/a5/f671e5cbec1c21d044ff3078223f949748f3a7f86b14e34a365d74a5d21f/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:c564dd5f09ddc9d8f2c2d0a301cd30a79a2cc1b46dd1a73bef8f0038863d016b", size = 1791630, upload-time = "2026-03-31T21:59:56.239Z" },
+ { url = "https://files.pythonhosted.org/packages/0b/63/fb8d0ad63a0b8a99be97deac8c04dacf0785721c158bdf23d679a87aa99e/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:2994be9f6e51046c4f864598fd9abeb4fba6e88f0b2152422c9666dcd4aea9c6", size = 1809403, upload-time = "2026-03-31T21:59:59.103Z" },
+ { url = "https://files.pythonhosted.org/packages/59/0c/bfed7f30662fcf12206481c2aac57dedee43fe1c49275e85b3a1e1742294/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:157826e2fa245d2ef46c83ea8a5faf77ca19355d278d425c29fda0beb3318037", size = 1634924, upload-time = "2026-03-31T22:00:02.116Z" },
+ { url = "https://files.pythonhosted.org/packages/17/d6/fd518d668a09fd5a3319ae5e984d4d80b9a4b3df4e21c52f02251ef5a32e/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:a8aca50daa9493e9e13c0f566201a9006f080e7c50e5e90d0b06f53146a54500", size = 1836119, upload-time = "2026-03-31T22:00:04.756Z" },
+ { url = "https://files.pythonhosted.org/packages/78/b7/15fb7a9d52e112a25b621c67b69c167805cb1f2ab8f1708a5c490d1b52fe/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:3b13560160d07e047a93f23aaa30718606493036253d5430887514715b67c9d9", size = 1772072, upload-time = "2026-03-31T22:00:07.494Z" },
+ { url = "https://files.pythonhosted.org/packages/7e/df/57ba7f0c4a553fc2bd8b6321df236870ec6fd64a2a473a8a13d4f733214e/aiohttp-3.13.5-cp314-cp314t-win32.whl", hash = "sha256:9a0f4474b6ea6818b41f82172d799e4b3d29e22c2c520ce4357856fced9af2f8", size = 471819, upload-time = "2026-03-31T22:00:10.277Z" },
+ { url = "https://files.pythonhosted.org/packages/62/29/2f8418269e46454a26171bfdd6a055d74febf32234e474930f2f60a17145/aiohttp-3.13.5-cp314-cp314t-win_amd64.whl", hash = "sha256:18a2f6c1182c51baa1d28d68fea51513cb2a76612f038853c0ad3c145423d3d9", size = 505441, upload-time = "2026-03-31T22:00:12.791Z" },
]
[[package]]
@@ -2529,8 +2529,8 @@ wheels = [
[[package]]
name = "reflex"
-version = "0.9.0a0.post13.dev0+bbb02193"
-source = { git = "https://github.com/reflex-dev/reflex?rev=main#bbb0219365c02bfa0f34fe028f2a8458952453b0" }
+version = "0.9.0a0.post14.dev0+57119bc3"
+source = { git = "https://github.com/reflex-dev/reflex?rev=main#57119bc3583695eca6bf3d99849bd5905a141902" }
dependencies = [
{ name = "alembic" },
{ name = "click" },
@@ -2565,8 +2565,8 @@ dependencies = [
[[package]]
name = "reflex-components-code"
-version = "0.0.0.post2926.dev0+bbb02193"
-source = { git = "https://github.com/reflex-dev/reflex?subdirectory=packages%2Freflex-components-code&rev=main#bbb0219365c02bfa0f34fe028f2a8458952453b0" }
+version = "0.0.0.post2927.dev0+57119bc3"
+source = { git = "https://github.com/reflex-dev/reflex?subdirectory=packages%2Freflex-components-code&rev=main#57119bc3583695eca6bf3d99849bd5905a141902" }
dependencies = [
{ name = "reflex-components-core" },
{ name = "reflex-components-lucide" },
@@ -2575,8 +2575,8 @@ dependencies = [
[[package]]
name = "reflex-components-core"
-version = "0.0.0.post2926.dev0+bbb02193"
-source = { git = "https://github.com/reflex-dev/reflex?subdirectory=packages%2Freflex-components-core&rev=main#bbb0219365c02bfa0f34fe028f2a8458952453b0" }
+version = "0.0.0.post2927.dev0+57119bc3"
+source = { git = "https://github.com/reflex-dev/reflex?subdirectory=packages%2Freflex-components-core&rev=main#57119bc3583695eca6bf3d99849bd5905a141902" }
dependencies = [
{ name = "python-multipart" },
{ name = "reflex-components-lucide" },
@@ -2587,26 +2587,26 @@ dependencies = [
[[package]]
name = "reflex-components-dataeditor"
-version = "0.0.0.post2926.dev0+bbb02193"
-source = { git = "https://github.com/reflex-dev/reflex?subdirectory=packages%2Freflex-components-dataeditor&rev=main#bbb0219365c02bfa0f34fe028f2a8458952453b0" }
+version = "0.0.0.post2927.dev0+57119bc3"
+source = { git = "https://github.com/reflex-dev/reflex?subdirectory=packages%2Freflex-components-dataeditor&rev=main#57119bc3583695eca6bf3d99849bd5905a141902" }
dependencies = [
{ name = "reflex-components-core" },
]
[[package]]
name = "reflex-components-gridjs"
-version = "0.0.0.post2926.dev0+bbb02193"
-source = { git = "https://github.com/reflex-dev/reflex?subdirectory=packages%2Freflex-components-gridjs&rev=main#bbb0219365c02bfa0f34fe028f2a8458952453b0" }
+version = "0.0.0.post2927.dev0+57119bc3"
+source = { git = "https://github.com/reflex-dev/reflex?subdirectory=packages%2Freflex-components-gridjs&rev=main#57119bc3583695eca6bf3d99849bd5905a141902" }
[[package]]
name = "reflex-components-lucide"
-version = "0.0.0.post2926.dev0+bbb02193"
-source = { git = "https://github.com/reflex-dev/reflex?subdirectory=packages%2Freflex-components-lucide&rev=main#bbb0219365c02bfa0f34fe028f2a8458952453b0" }
+version = "0.0.0.post2927.dev0+57119bc3"
+source = { git = "https://github.com/reflex-dev/reflex?subdirectory=packages%2Freflex-components-lucide&rev=main#57119bc3583695eca6bf3d99849bd5905a141902" }
[[package]]
name = "reflex-components-markdown"
-version = "0.0.0.post2926.dev0+bbb02193"
-source = { git = "https://github.com/reflex-dev/reflex?subdirectory=packages%2Freflex-components-markdown&rev=main#bbb0219365c02bfa0f34fe028f2a8458952453b0" }
+version = "0.0.0.post2927.dev0+57119bc3"
+source = { git = "https://github.com/reflex-dev/reflex?subdirectory=packages%2Freflex-components-markdown&rev=main#57119bc3583695eca6bf3d99849bd5905a141902" }
dependencies = [
{ name = "reflex-components-code" },
{ name = "reflex-components-core" },
@@ -2615,21 +2615,21 @@ dependencies = [
[[package]]
name = "reflex-components-moment"
-version = "0.0.0.post2926.dev0+bbb02193"
-source = { git = "https://github.com/reflex-dev/reflex?subdirectory=packages%2Freflex-components-moment&rev=main#bbb0219365c02bfa0f34fe028f2a8458952453b0" }
+version = "0.0.0.post2927.dev0+57119bc3"
+source = { git = "https://github.com/reflex-dev/reflex?subdirectory=packages%2Freflex-components-moment&rev=main#57119bc3583695eca6bf3d99849bd5905a141902" }
[[package]]
name = "reflex-components-plotly"
-version = "0.0.0.post2926.dev0+bbb02193"
-source = { git = "https://github.com/reflex-dev/reflex?subdirectory=packages%2Freflex-components-plotly&rev=main#bbb0219365c02bfa0f34fe028f2a8458952453b0" }
+version = "0.0.0.post2927.dev0+57119bc3"
+source = { git = "https://github.com/reflex-dev/reflex?subdirectory=packages%2Freflex-components-plotly&rev=main#57119bc3583695eca6bf3d99849bd5905a141902" }
dependencies = [
{ name = "reflex-components-core" },
]
[[package]]
name = "reflex-components-radix"
-version = "0.0.0.post2926.dev0+bbb02193"
-source = { git = "https://github.com/reflex-dev/reflex?subdirectory=packages%2Freflex-components-radix&rev=main#bbb0219365c02bfa0f34fe028f2a8458952453b0" }
+version = "0.0.0.post2927.dev0+57119bc3"
+source = { git = "https://github.com/reflex-dev/reflex?subdirectory=packages%2Freflex-components-radix&rev=main#57119bc3583695eca6bf3d99849bd5905a141902" }
dependencies = [
{ name = "reflex-components-core" },
{ name = "reflex-components-lucide" },
@@ -2637,29 +2637,29 @@ dependencies = [
[[package]]
name = "reflex-components-react-player"
-version = "0.0.0.post2926.dev0+bbb02193"
-source = { git = "https://github.com/reflex-dev/reflex?subdirectory=packages%2Freflex-components-react-player&rev=main#bbb0219365c02bfa0f34fe028f2a8458952453b0" }
+version = "0.0.0.post2927.dev0+57119bc3"
+source = { git = "https://github.com/reflex-dev/reflex?subdirectory=packages%2Freflex-components-react-player&rev=main#57119bc3583695eca6bf3d99849bd5905a141902" }
dependencies = [
{ name = "reflex-components-core" },
]
[[package]]
name = "reflex-components-recharts"
-version = "0.0.0.post2926.dev0+bbb02193"
-source = { git = "https://github.com/reflex-dev/reflex?subdirectory=packages%2Freflex-components-recharts&rev=main#bbb0219365c02bfa0f34fe028f2a8458952453b0" }
+version = "0.0.0.post2927.dev0+57119bc3"
+source = { git = "https://github.com/reflex-dev/reflex?subdirectory=packages%2Freflex-components-recharts&rev=main#57119bc3583695eca6bf3d99849bd5905a141902" }
[[package]]
name = "reflex-components-sonner"
-version = "0.0.0.post2926.dev0+bbb02193"
-source = { git = "https://github.com/reflex-dev/reflex?subdirectory=packages%2Freflex-components-sonner&rev=main#bbb0219365c02bfa0f34fe028f2a8458952453b0" }
+version = "0.0.0.post2927.dev0+57119bc3"
+source = { git = "https://github.com/reflex-dev/reflex?subdirectory=packages%2Freflex-components-sonner&rev=main#57119bc3583695eca6bf3d99849bd5905a141902" }
dependencies = [
{ name = "reflex-components-lucide" },
]
[[package]]
name = "reflex-core"
-version = "0.0.0.post2926.dev0+bbb02193"
-source = { git = "https://github.com/reflex-dev/reflex?subdirectory=packages%2Freflex-core&rev=main#bbb0219365c02bfa0f34fe028f2a8458952453b0" }
+version = "0.0.0.post2927.dev0+57119bc3"
+source = { git = "https://github.com/reflex-dev/reflex?subdirectory=packages%2Freflex-core&rev=main#57119bc3583695eca6bf3d99849bd5905a141902" }
dependencies = [
{ name = "packaging" },
{ name = "platformdirs" },
@@ -2670,8 +2670,8 @@ dependencies = [
[[package]]
name = "reflex-docgen"
-version = "0.0.0.post2926.dev0+bbb02193"
-source = { git = "https://github.com/reflex-dev/reflex?subdirectory=packages%2Freflex-docgen&rev=main#bbb0219365c02bfa0f34fe028f2a8458952453b0" }
+version = "0.0.0.post2927.dev0+57119bc3"
+source = { git = "https://github.com/reflex-dev/reflex?subdirectory=packages%2Freflex-docgen&rev=main#57119bc3583695eca6bf3d99849bd5905a141902" }
dependencies = [
{ name = "griffelib" },
{ name = "mistletoe" },