From 0b89fd10f07f1e21278567ed30ad024f90ec3426 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 7 Nov 2025 07:31:05 +0000 Subject: [PATCH 1/3] docs: Update for Ludic 1.0 Python 3.14 and t-strings Update documentation to reflect Ludic 1.0 requirements: - Change Python version requirement from 3.12+ to 3.14+ - Update all references from f-strings to t-strings - Update Python badge to show 3.14+ - Update getting started guide to use Python 3.14 These changes align with Ludic 1.0's new requirements for Python 3.14+ and its adoption of t-strings (new Python 3.14 feature). --- web/endpoints/docs/components.py | 20 ++++++++++---------- web/endpoints/docs/getting_started.py | 2 +- web/endpoints/docs/index.py | 12 ++++++------ 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/web/endpoints/docs/components.py b/web/endpoints/docs/components.py index 370b142..fdbd728 100644 --- a/web/endpoints/docs/components.py +++ b/web/endpoints/docs/components.py @@ -410,12 +410,12 @@ def render(self) -> td: """, language="python", ), - H3("Using f-strings"), + H3("Using t-strings"), Paragraph( - "In Ludic, f-strings offer a bit more readable way to construct component " + "In Ludic, t-strings offer a bit more readable way to construct component " "content, especially if you need to do a lot of formatting with " f"{Code("")}, {Code("")}, and other elements for improving " - "typography. Let's modify the previous example using f-strings:" + "typography. Let's modify the previous example using t-strings:" ), CodeBlock( """ @@ -428,22 +428,22 @@ def render(self) -> td: ), b("Important Note: Memory Considerations"), Paragraph( - f"{i("Temporary Dictionaries")}: to make f-strings safely work, they " + f"{i("Temporary Dictionaries")}: to make t-strings safely work, they " "internally create temporary dictionaries to hold the component " "instances. To avoid memory leaks, these dictionaries need to be " "consumed by a component." ), Paragraph("There are two cases it can create hanging objects (memory leaks):"), NumberedList( - Item("Component initialization with the f-string fails."), + Item("Component initialization with the t-string fails."), Item( - "You store an f-string in a variable but don't pass it to a " + "You store a t-string in a variable but don't pass it to a " "component." ), ), MessageWarning( Title("Possible memory leak"), - "The implementation of f-strings requires the creation of a temporary dict " + "The implementation of t-strings requires the creation of a temporary dict " "which can result in hanging objects in memory. To avoid memory leaks, " f"there is the {Code('BaseElement.formatter')} attribute which is a " "context manager clearing the temporary dict on exit.", @@ -454,7 +454,7 @@ def render(self) -> td: from ludic.base import BaseElement with BaseElement.formatter: - # you can do anything with f-strings here, no memory leak + # you can do anything with t-strings here, no memory leak # is created since formatter dict is cleared on exit """, language="python", @@ -463,11 +463,11 @@ def render(self) -> td: Paragraph( "The Ludic Web Framework (built on Starlette) automatically wraps request " f"handlers with {Code("BaseElement.formatter")}, providing a safe " - "environment for f-strings.", + "environment for t-strings.", ), b("Key Takeaways"), Paragraph( - "While f-strings are convenient, exercise caution to prevent memory leaks. " + "While t-strings are convenient, exercise caution to prevent memory leaks. " "Use them within the provided safety mechanisms. In contexts like task " "queues or other web frameworks, you can use a similar mechanism of " "wrapping to achieve memory safety." diff --git a/web/endpoints/docs/getting_started.py b/web/endpoints/docs/getting_started.py index 9b90223..ac5d504 100644 --- a/web/endpoints/docs/getting_started.py +++ b/web/endpoints/docs/getting_started.py @@ -44,7 +44,7 @@ def getting_started(request: Request) -> Page: "Navigate to the new project's directory and install the required " "dependencies using UV:" ), - CodeBlock("uv sync --python 3.13"), + CodeBlock("uv sync --python 3.14"), ), Item( b("Run the Project"), diff --git a/web/endpoints/docs/index.py b/web/endpoints/docs/index.py index 16d2eb9..089b470 100644 --- a/web/endpoints/docs/index.py +++ b/web/endpoints/docs/index.py @@ -31,9 +31,9 @@ def index(request: Request) -> Page: title="Code Coverage", ), Badge( - url="https://www.python.org/downloads/release/python-3130/", - img_url="https://img.shields.io/badge/Python-3.12%20%7C%203.13-blue.svg", - title="Python 3.12 and 3.13", + url="https://www.python.org/downloads/release/python-3140/", + img_url="https://img.shields.io/badge/Python-3.14%2B-blue.svg", + title="Python 3.14+", ), Badge( url="https://mypy-lang.org/", @@ -63,7 +63,7 @@ def index(request: Request) -> Page: f"wrapper around the powerful { Link('Starlette', to='https://www.starlette.io/') } framework. It is built " - "with the latest Python 3.12 features heavily incorporating typing." + "with the latest Python 3.14 features heavily incorporating typing." ), H2("Features"), List( @@ -76,7 +76,7 @@ def index(request: Request) -> Page: f"Uses the power of {b('Starlette')} and {b('Async')} for " "high-performance web development" ), - Item(f"Build HTML with the ease and power of Python {b('f-strings')}"), + Item(f"Build HTML with the ease and power of Python {b('t-strings')}"), Item(f"Add CSS styling to your components with {b('Themes')}"), Item( "Create simple, responsive layouts adopted from the " @@ -208,7 +208,7 @@ def Counter(number: int) -> Cluster: language="python", ), H2("Requirements"), - Paragraph("Python 3.12+"), + Paragraph("Python 3.14+"), H2("Installation"), Paragraph( "The following command installs Ludic with Starlette as ASGI framework. " From f90c77aecbaf9fbb9d1521063fce243147b619c7 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 7 Nov 2025 07:33:33 +0000 Subject: [PATCH 2/3] docs: Add notes about Ludic 0.5.x compatibility Add informational notes to documentation about Ludic 0.5.x: - Clarify that Ludic 0.5.x supports Python 3.12+ with f-strings - Help users understand version differences between 0.5.x and 1.0 - Provide guidance for users on older Python versions This helps users make informed decisions about which version to use based on their Python version and whether they want to use t-strings. --- web/endpoints/docs/getting_started.py | 7 +++++++ web/endpoints/docs/index.py | 5 +++++ 2 files changed, 12 insertions(+) diff --git a/web/endpoints/docs/getting_started.py b/web/endpoints/docs/getting_started.py index ac5d504..4418649 100644 --- a/web/endpoints/docs/getting_started.py +++ b/web/endpoints/docs/getting_started.py @@ -17,6 +17,13 @@ def getting_started(request: Request) -> Page: to="https://github.com/getludic/template")}. Follow the steps " "below to set up your project quickly." ), + Paragraph( + f"{b('Note')}: This guide covers Ludic 1.0 which requires Python 3.14+ and " + "uses t-strings. If you're using Python 3.12 or 3.13, use Ludic 0.5.x with " + f"f-strings instead. Install with {Code('--python 3.13')} and use f-strings " + "in your code.", + style={"font-size": "0.9em", "color": "#666", "padding": "1em", "background": "#f5f5f5", "border-radius": "4px"}, + ), H2("Installation Steps"), NumberedList( Item( diff --git a/web/endpoints/docs/index.py b/web/endpoints/docs/index.py index 089b470..f64397c 100644 --- a/web/endpoints/docs/index.py +++ b/web/endpoints/docs/index.py @@ -209,6 +209,11 @@ def Counter(number: int) -> Cluster: ), H2("Requirements"), Paragraph("Python 3.14+"), + Paragraph( + f"{b('Note')}: Ludic 0.5.x supports Python 3.12+ with f-strings. " + "For Python 3.12 or 3.13, use Ludic 0.5.x and f-strings instead of t-strings.", + style={"font-size": "0.9em", "color": "#666"}, + ), H2("Installation"), Paragraph( "The following command installs Ludic with Starlette as ASGI framework. " From f3dfd2deb1505172c7473e2448b258eefd42415f Mon Sep 17 00:00:00 2001 From: Pavel Dedik Date: Fri, 7 Nov 2025 09:01:55 +0100 Subject: [PATCH 3/3] chore: Fix f-stings in docs --- .pre-commit-config.yaml | 10 +- pyproject.toml | 3 + web/components.py | 16 +- web/endpoints/catalog/buttons.py | 10 +- web/endpoints/catalog/forms.py | 48 +++-- web/endpoints/catalog/index.py | 44 ++-- web/endpoints/catalog/layouts.py | 8 +- web/endpoints/catalog/messages.py | 4 +- web/endpoints/catalog/tables.py | 20 +- web/endpoints/catalog/typography.py | 15 +- web/endpoints/docs/components.py | 217 ++++++++++---------- web/endpoints/docs/getting_started.py | 33 +-- web/endpoints/docs/htmx.py | 56 ++--- web/endpoints/docs/index.py | 5 +- web/endpoints/docs/styles.py | 103 +++++----- web/endpoints/docs/web_framework.py | 130 ++++++------ web/endpoints/examples/bulk_update.py | 28 +-- web/endpoints/examples/cascading_selects.py | 2 +- web/endpoints/examples/click_to_edit.py | 20 +- web/endpoints/examples/click_to_load.py | 18 +- web/endpoints/examples/complex_layout.py | 2 +- web/endpoints/examples/delete_row.py | 8 +- web/endpoints/examples/edit_row.py | 10 +- web/endpoints/examples/index.py | 10 +- web/endpoints/examples/infinite_scroll.py | 19 +- web/endpoints/examples/lazy_loading.py | 2 +- web/endpoints/index.py | 10 +- web/search/index.py | 2 +- 28 files changed, 446 insertions(+), 407 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 54ba8f4..a068490 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -10,7 +10,7 @@ default_stages: repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.5.0 + rev: v6.0.0 hooks: - id: trailing-whitespace - id: end-of-file-fixer @@ -21,18 +21,14 @@ repos: - id: detect-private-key - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.4.5 + rev: v0.14.4 hooks: - id: ruff-format - - - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.4.5 - hooks: - id: ruff args: ["--fix", "--exit-non-zero-on-fix", "--config", "pyproject.toml"] - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.10.1 + rev: v1.18.2 hooks: - id: mypy args: ["--ignore-missing-imports"] diff --git a/pyproject.toml b/pyproject.toml index 6b7820b..a6d14ae 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -44,6 +44,9 @@ dev = [ line-length = 88 target-version = "py312" +[tool.ruff.format] +quote-style = "double" + [tool.ruff.lint] select = [ "E", # flake8 rules diff --git a/web/components.py b/web/components.py index f7e955b..9abe042 100644 --- a/web/components.py +++ b/web/components.py @@ -260,11 +260,11 @@ class Footer(Component[NoChildren, NoAttrs]): @override def render(self) -> Box: return Box( - f"Made with {b(Link("Ludic", to=config.GITHUB_REPO_URL))} " - f"and {b(Link("HTMX", to="https://htmx.org"))} and 🐍 " - f"• {b(Link("Discord", to=config.DISCORD_INVITE_URL))} " - f"• {b(Link("GitHub", to=config.GITHUB_REPO_URL))} " - f"• Running on {b(Link("Leapcell", to="https://leapcell.io"))} " + f"Made with {b(Link('Ludic', to=config.GITHUB_REPO_URL))} " + f"and {b(Link('HTMX', to='https://htmx.org'))} and 🐍 " + f"• {b(Link('Discord', to=config.DISCORD_INVITE_URL))} " + f"• {b(Link('GitHub', to=config.GITHUB_REPO_URL))} " + f"• Running on {b(Link('Leapcell', to='https://leapcell.io'))} " ) @@ -365,9 +365,9 @@ class EditOnGithub(Component[AnyChildren, EditOnGithubAttrs]): @override def render(self) -> div: file_path = ( - f"{self.attrs["base_url"].replace("-", "_")}index.py" + f"{self.attrs['base_url'].replace('-', '_')}index.py" if self.attrs["base_url"].endswith("/") - else f"{self.attrs["base_url"].replace("-", "_")}.py" + else f"{self.attrs['base_url'].replace('-', '_')}.py" ) url = f"{config.GITHUB_REPO_WEB_URL}/blob/main/web/endpoints{file_path}" return div( @@ -407,6 +407,6 @@ def render(self) -> blockquote: return blockquote( div( p(i('"', *self.children, '"')), - p(b(f"– {self.attrs["source"]}")), + p(b(f"– {self.attrs['source']}")), ), ) diff --git a/web/endpoints/catalog/buttons.py b/web/endpoints/catalog/buttons.py index b207cc0..20da99f 100644 --- a/web/endpoints/catalog/buttons.py +++ b/web/endpoints/catalog/buttons.py @@ -20,7 +20,7 @@ def buttons(request: Request) -> Page: return Page( H1("Buttons"), Paragraph( - f"The module {Code("ludic.catalog.buttons")} contains the following " + f"The module {Code('ludic.catalog.buttons')} contains the following " "buttons:" ), Box( @@ -61,8 +61,8 @@ def buttons(request: Request) -> Page: language="python", ), Paragraph( - f"The {Code("ButtonLink")} component can be styled like any other " - f"button using the {Code("classes")} attribute:" + f"The {Code('ButtonLink')} component can be styled like any other " + f"button using the {Code('classes')} attribute:" ), CodeBlock( """ @@ -72,7 +72,7 @@ def buttons(request: Request) -> Page: """, language="python", ), - Paragraph(f"You can disable a button using the {Code("disabled")} attribute:"), + Paragraph(f"You can disable a button using the {Code('disabled')} attribute:"), Box( Cluster( Button("Button", disabled=True), @@ -96,7 +96,7 @@ def buttons(request: Request) -> Page: H2("Button Sizes"), Paragraph( "You can also change the size of the button appending the " - f"{Code("small")} or {Code("large")} class:" + f"{Code('small')} or {Code('large')} class:" ), Box( Cluster( diff --git a/web/endpoints/catalog/forms.py b/web/endpoints/catalog/forms.py index 8482001..cee76ef 100644 --- a/web/endpoints/catalog/forms.py +++ b/web/endpoints/catalog/forms.py @@ -29,15 +29,15 @@ def forms(request: Request) -> Page: return Page( H1("Forms"), Paragraph( - f"These components located in {Code("ludic.catalog.forms")} are in an " + f"These components located in {Code('ludic.catalog.forms')} are in an " "experimental mode. There is the possibility to automatically create form " - f"{Link("fields from annotations", to="#generating-form-fields")}, but it " + f"{Link('fields from annotations', to='#generating-form-fields')}, but it " "is far from production-ready." ), H2("Input Field"), Paragraph( - f"The {Code("InputField")} component is the most basic form field. It " - f"is a wrapper around the {Code("input")} HTML element which also " + f"The {Code('InputField')} component is the most basic form field. It " + f"is a wrapper around the {Code('input')} HTML element which also " "generates a label if not disabled. Here is what it looks like:" ), InputField( @@ -59,7 +59,7 @@ def forms(request: Request) -> Page: ), Paragraph( "You can also create the input field without the label by not passing " - f"the {Code("label")} attribute:" + f"the {Code('label')} attribute:" ), InputField(name="sample-input-field"), CodeBlock( @@ -74,8 +74,8 @@ def forms(request: Request) -> Page: ), H2("Select Field"), Paragraph( - f"The {Code("SelectField")} component is a wrapper around the " - f"{Code("select")} HTML element. It also generates a label if not " + f"The {Code('SelectField')} component is a wrapper around the " + f"{Code('select')} HTML element. It also generates a label if not " "disabled. Here is what it looks like:" ), SelectField( @@ -101,8 +101,8 @@ def forms(request: Request) -> Page: ), H2("Text Area"), Paragraph( - f"The {Code("TextAreaField")} component is a wrapper around the " - f"{Code("textarea")} HTML element. It also generates a label if not " + f"The {Code('TextAreaField')} component is a wrapper around the " + f"{Code('textarea')} HTML element. It also generates a label if not " "disabled. Here is what it looks like:" ), TextAreaField( @@ -126,7 +126,7 @@ def forms(request: Request) -> Page: ), H2("Choices"), Paragraph( - f"The {Code("ChoiceField")} component is a wrapper around the " + f"The {Code('ChoiceField')} component is a wrapper around the " f"{Code("")} HTML element. It also generates a label " "if not disabled. Here is what it looks like:" ), @@ -151,7 +151,7 @@ def forms(request: Request) -> Page: ), H2("Form"), Paragraph( - f"The {Code("Form")} component is a wrapper around the {Code("form")} " + f"The {Code('Form')} component is a wrapper around the {Code('form')} " "HTML element. Here is a sample form:" ), Form( @@ -188,9 +188,10 @@ def forms(request: Request) -> Page: language="python", ), Paragraph( - f"We use the {Code("Cluster")} component to create a form with multiple " - f"input fields and buttons. this component is described in the {Link( - "Layouts section", to=request.url_for("catalog:layouts").path)}. " + f"We use the {Code('Cluster')} component to create a form with multiple " + f"input fields and buttons. this component is described in the { + Link('Layouts section', to=request.url_for('catalog:layouts').path) + }. " "You can also have the input field and the button aligned horizontally " "using this layout component:" ), @@ -229,7 +230,7 @@ def forms(request: Request) -> Page: Paragraph( "In some cases, the attributes of a component or class-based endpoint " "can be used to create form fields automatically using the " - f"{Code("create_fields")} function. Here is an example:" + f"{Code('create_fields')} function. Here is an example:" ), Form( *create_fields( @@ -253,9 +254,9 @@ class PersonAttrs(Attrs): language="python", ), Paragraph( - f"The {Code("create_fields")} function generates form fields from " + f"The {Code('create_fields')} function generates form fields from " "annotations. It generates only fields that are annotated with the " - f"{Code("FieldMeta")} dataclass, which looks like this:" + f"{Code('FieldMeta')} dataclass, which looks like this:" ), CodeBlock( """ @@ -270,7 +271,7 @@ class FieldMeta: language="python", ), Paragraph( - f"The {Code("parser")} attribute validates and parses the field. Here is " + f"The {Code('parser')} attribute validates and parses the field. Here is " "how you would use it:" ), CodeBlock( @@ -296,10 +297,13 @@ class CustomerAttrs(Attrs): language="python", ), Paragraph( - f"Fields created with the {Code("create_fields")} function can be than " - f"extracted from submitted form data using the {Link( - "Parser class", - to=f"{request.url_for("docs:web_framework").path}#parsers")}." + f"Fields created with the {Code('create_fields')} function can be than " + f"extracted from submitted form data using the { + Link( + 'Parser class', + to=f'{request.url_for("docs:web_framework").path}#parsers', + ) + }." ), request=request, active_item="forms", diff --git a/web/endpoints/catalog/index.py b/web/endpoints/catalog/index.py index e7eeea1..bf6ffb1 100644 --- a/web/endpoints/catalog/index.py +++ b/web/endpoints/catalog/index.py @@ -15,7 +15,7 @@ def index(request: Request) -> Page: return Page( H1("Catalog"), Paragraph( - f"The {Code("ludic.catalog")} module is a collection of components " + f"The {Code('ludic.catalog')} module is a collection of components " "designed to help build applications using the Ludic framework. It " "serves as both a resource for building new web applications and also " "as a showcase of ways to implement components." @@ -23,14 +23,14 @@ def index(request: Request) -> Page: Paragraph( "Each item in the catalog is a reusable component that generates HTML " "code and registers its own CSS styles.. The registered CSS are loaded " - f"using the {Code("style.load()")} method, as detailed in the " - f"{Link("Styles and Themes", to=request.url_path_for("docs:styles"))} " + f"using the {Code('style.load()')} method, as detailed in the " + f"{Link('Styles and Themes', to=request.url_path_for('docs:styles'))} " "section of the documentation." ), Paragraph( - f"The catalog components are like {b("Lego pieces")} you can assemble " - f"together to build interactive and beautiful {b("HTML documents")} " - f"with {b("minimalistic")} approach:" + f"The catalog components are like {b('Lego pieces')} you can assemble " + f"together to build interactive and beautiful {b('HTML documents')} " + f"with {b('minimalistic')} approach:" ), List( Item( @@ -41,13 +41,13 @@ def index(request: Request) -> Page: "The generated CSS is simple, extensible, and easy to understand. " "The layouts you can use for building your pages are responsive, " "reusable, and robust. They are based on the amazing " - f"{Link("Every Layout Book", to="https://every-layout.dev/")}." + f"{Link('Every Layout Book', to='https://every-layout.dev/')}." ), ), H2("How Do You Use The Catalog?"), Paragraph( f"In order for everything to work correctly, the first thing you usually " - f"need to do is to create a {Code("Page")} component. This component is " + f"need to do is to create a {Code('Page')} component. This component is " "important for two reasons:" ), List( @@ -62,37 +62,37 @@ def index(request: Request) -> Page: Message( Title("How does CSS loading work?"), f"The CSS styles for components are loaded when the component is imported " - f"anywhere in your application. The {Code("style.load()")} method iterates " + f"anywhere in your application. The {Code('style.load()')} method iterates " "over all imported components and checks if the components have any styles " "defined.", ), Paragraph( "All rendered HTML documents will have this component as a base similar to " - f"how all HTML pages in template engines like Jinja2 use the {i("base")} " + f"how all HTML pages in template engines like Jinja2 use the {i('base')} " "template." ), Message( Title( - f"How does a {Code("Page")} component differ from a regular component?" + f"How does a {Code('Page')} component differ from a regular component?" ), "The only difference is that it renders as a valid HTML5 document starting " - f"with the {Code("")} declaration. You usually need only " - f"one {Code("Page")} component in the whole application.", + f"with the {Code('')} declaration. You usually need only " + f"one {Code('Page')} component in the whole application.", ), Paragraph( - f"After you are done with setting up your {Code("Page")} component, you " + f"After you are done with setting up your {Code('Page')} component, you " "can use it along with all the other components in the catalog." ), H3("HtmlPage Component"), Paragraph( "This component has already been mentioned throughout the documentation " - f"and can be used to create your {Code("Page")} component. The " - f"{Code("HtmlPage")} component is just for convenience so that you can " + f"and can be used to create your {Code('Page')} component. The " + f"{Code('HtmlPage')} component is just for convenience so that you can " "quickly start and not worry about how to load e.g. CSS styles or HTMX. " ), Paragraph( - f"Here is how you would use the {Code("HtmlPage")} component to " - f"create your own {Code("Page")} component:" + f"Here is how you would use the {Code('HtmlPage')} component to " + f"create your own {Code('Page')} component:" ), CodeBlock( """ @@ -143,7 +143,7 @@ def render(self) -> HtmlPage: Item(Code('htmx_version="latest"')), ), Paragraph( - f"Now that you prepared your {Code("Page")} component, you can use it in " + f"Now that you prepared your {Code('Page')} component, you can use it in " "your code like here:" ), CodeBlock( @@ -171,9 +171,9 @@ def clicked(request: Request) -> Paragraph: language="python", ), Paragraph( - f"The {Code("HtmlPage")} component comes also with default " - f"{Code("styles")}. In fact, the following list of styles are " - f"auto-loaded whenever you import anything from the {Code("ludic.catalog")}" + f"The {Code('HtmlPage')} component comes also with default " + f"{Code('styles')}. In fact, the following list of styles are " + f"auto-loaded whenever you import anything from the {Code('ludic.catalog')}" " module:" ), List( diff --git a/web/endpoints/catalog/layouts.py b/web/endpoints/catalog/layouts.py index ef2bfbf..305441e 100644 --- a/web/endpoints/catalog/layouts.py +++ b/web/endpoints/catalog/layouts.py @@ -27,7 +27,7 @@ def layouts(request: Request) -> Page: H1("Layouts"), Paragraph( "Layouts create a flexible structure for your user interface. " - f"The layouts in this module are based on the {b("Every Layout Book")}. " + f"The layouts in this module are based on the {b('Every Layout Book')}. " "If you haven't read the book, go read it now: " ), List(Item(Link("Every Layout Book", to="https://every-layout.dev/"))), @@ -37,7 +37,7 @@ def layouts(request: Request) -> Page: "The layouts can be combined together." ), Paragraph( - f"All layouts are located in the {Code("ludic.catalog.layouts")} module." + f"All layouts are located in the {Code('ludic.catalog.layouts')} module." ), Message( Title("Viewing this page on mobile devices"), @@ -51,7 +51,7 @@ def layouts(request: Request) -> Page: "you can easily combine to create responsive, readable, and maintainable " "user interfaces. Instead of starting from scratch with basic HTML " "elements and manually writing CSS, you use these layouts as your " - f"{b("foundational building blocks")}. This approach allows you to compose " + f"{b('foundational building blocks')}. This approach allows you to compose " "and arrange them in various ways to design your desired user interface, " "making your code more organized and easier to manage." ), @@ -59,7 +59,7 @@ def layouts(request: Request) -> Page: Paragraph( "When writing CSS, you might be tempted to apply e.g. specific spacing, " "colors, or font styles to an HTML element like a button. The idea of " - f"using the layout classes here is to define {b("context")} which " + f"using the layout classes here is to define {b('context')} which " "determines how an HTML element should be displayed. So for example, we " "might want to have a couple of buttons on one line, so we wrap them in " "a Cluster class which inserts a margin between all its children and puts " diff --git a/web/endpoints/catalog/messages.py b/web/endpoints/catalog/messages.py index 4df8ae8..6daf84c 100644 --- a/web/endpoints/catalog/messages.py +++ b/web/endpoints/catalog/messages.py @@ -17,7 +17,7 @@ def messages(request: Request) -> Page: return Page( H1("Messages"), Paragraph( - f"The module {Code("ludic.catalog.messages")} contains the following " + f"The module {Code('ludic.catalog.messages')} contains the following " "components:" ), Message( @@ -70,7 +70,7 @@ def messages(request: Request) -> Page: language="python", ), Paragraph( - f"Note that you don't need to specify the {Code("Title")} component. " + f"Note that you don't need to specify the {Code('Title')} component. " "Here is how the message renders without the title:" ), Message( diff --git a/web/endpoints/catalog/tables.py b/web/endpoints/catalog/tables.py index 8da16d5..9580c7b 100644 --- a/web/endpoints/catalog/tables.py +++ b/web/endpoints/catalog/tables.py @@ -38,15 +38,15 @@ def tables(request: Request) -> Page: return Page( H1("Tables"), Paragraph( - f"These components located in {Code("ludic.catalog.tables")} are in an " + f"These components located in {Code('ludic.catalog.tables')} are in an " "experimental mode. There is the possibility to automatically create " "tables even containing form fields and actions from annotations, but " "it is far from production-ready." ), H2("Creating a Table"), Paragraph( - f"The {Code("Table")} component accepts a {Code("TableHead")} as the first " - f"item and any number of {Code("TableRow")} items." + f"The {Code('Table')} component accepts a {Code('TableHead')} as the first " + f"item and any number of {Code('TableRow')} items." ), Table( TableHead("First Name", "Last Name", "Age"), @@ -91,7 +91,7 @@ def tables(request: Request) -> Page: ), H2("Generating Table Rows"), Paragraph( - f"The {Code("create_rows")} function can be used to generate rows in the " + f"The {Code('create_rows')} function can be used to generate rows in the " "table based on given specification. Here is an example:" ), CodeBlock( @@ -125,7 +125,7 @@ class PersonAttrs(Attrs): Paragraph( "There is also an experimental support for tables containing form fields." "The way it works is that you wrap your table in a form and use the " - f"{Code("create_rows")} function to generate the rows. Here is an example:" + f"{Code('create_rows')} function to generate the rows. Here is an example:" ), Form( Table(*create_rows(contacts_attrs_list, spec=SampleContactAttrs)), @@ -161,10 +161,12 @@ class ContactAttrs(Attrs): ), Paragraph( "The role of the identifier column is to be able to parse the form data " - f"using the {Code("ListParser")} function as documented in the {Link( - "Parsers section", - to=f"{request.url_for("docs:web_framework").path}#parsing-collections" - )} of the documentation." + f"using the {Code('ListParser')} function as documented in the { + Link( + 'Parsers section', + to=f'{request.url_for("docs:web_framework").path}#parsing-collections', + ) + } of the documentation." ), request=request, active_item="tables", diff --git a/web/endpoints/catalog/typography.py b/web/endpoints/catalog/typography.py index 32f2d4e..b691b18 100644 --- a/web/endpoints/catalog/typography.py +++ b/web/endpoints/catalog/typography.py @@ -11,13 +11,14 @@ def typography(request: Request) -> Page: return Page( H1("Typography"), Paragraph( - f"The module {Code("ludic.catalog.typography")} contains the following " + f"The module {Code('ludic.catalog.typography')} contains the following " "components:" ), H4("Link"), Paragraph( - f"This text contains a {Link( - "link", to=str(request.url_for("catalog:index")))}.", + f"This text contains a { + Link('link', to=str(request.url_for('catalog:index'))) + }.", ), CodeBlock( """ @@ -39,7 +40,7 @@ def typography(request: Request) -> Page: language="python", ), H4("Code"), - Paragraph(f"This text contains code: {Code("let mut x = 0;")}"), + Paragraph(f"This text contains code: {Code('let mut x = 0;')}"), CodeBlock( """ from ludic.catalog.typography import Code @@ -134,7 +135,7 @@ def typography(request: Request) -> Page: ), H2("Headers"), Paragraph( - f"The module {Code("ludic.catalog.headers")} contains components " + f"The module {Code('ludic.catalog.headers')} contains components " "rendering as HTML headers (h1-h4)." ), H1("H1", anchor=False), @@ -153,7 +154,7 @@ def typography(request: Request) -> Page: language="python", ), Paragraph( - f"The module also contains the {Code("Anchor")} component, which can be " + f"The module also contains the {Code('Anchor')} component, which can be " "used to create an anchor link next to the header." ), H4("H4 With Anchor", anchor=Anchor("#", target="h4-with-anchor")), @@ -167,7 +168,7 @@ def typography(request: Request) -> Page: ), Paragraph( "It is possible to generate the anchor automatically using the " - f"{Code("anchor=True")} attribute:" + f"{Code('anchor=True')} attribute:" ), H3("H3 With Anchor", anchor=True), CodeBlock( diff --git a/web/endpoints/docs/components.py b/web/endpoints/docs/components.py index fdbd728..756d012 100644 --- a/web/endpoints/docs/components.py +++ b/web/endpoints/docs/components.py @@ -19,26 +19,26 @@ def components(request: Request) -> Page: H2("Key Concepts"), List( Item( - f"{b("Components")}: a component is a reusable chunk of code that " + f"{b('Components')}: a component is a reusable chunk of code that " "defines a piece of your user interface. Think of it like a blueprint " "for an HTML element, but more powerful." ), Item( - f"{b("Elements")}: these represent the individual HTML tags (like " - f"{Code("")}, {Code("
")}, {Code("

")}, etc.) that make up " + f"{b('Elements')}: these represent the individual HTML tags (like " + f"{Code('')}, {Code('
')}, {Code('

')}, etc.) that make up " "the structure of your page." ), Item( - f"{b("Attributes")}: These help define the properties on your " + f"{b('Attributes')}: These help define the properties on your " "components and elements. They let you modify things like a link's " "destination, text color, or an element's size." ), Item( - f"{b("Hierarchy")}: Components can contain other components or " + f"{b('Hierarchy')}: Components can contain other components or " "elements, creating a tree-like structure." ), Item( - f"{b("Types")}: A safety net to help you write correct code, " + f"{b('Types')}: A safety net to help you write correct code, " "preventing errors just like making sure LEGO pieces fit together " "properly." ), @@ -46,18 +46,18 @@ def components(request: Request) -> Page: H2("Types of Components"), List( Item( - f"{b("Regular")}: These are flexible, letting you have multiple " + f"{b('Regular')}: These are flexible, letting you have multiple " "children of any type." ), Item( - f"{b("Strict")}: Perfect for when you need precise control over the " + f"{b('Strict')}: Perfect for when you need precise control over the " "structure of your component – like a table where you must have a head " "and a body." ), ), H2("Regular Components"), Paragraph( - f"Let's break down a simplified version of the {Code("Link")} component:" + f"Let's break down a simplified version of the {Code('Link')} component:" ), CodeBlock( """ @@ -80,27 +80,27 @@ def render(self): ), List( Item( - f"{i("HTML Rendering")}: This component renders as the following HTML " + f"{i('HTML Rendering')}: This component renders as the following HTML " f"element:", List(Item(Code('...'))), ), Item( - f"{i("Type Hints")}: {Code("Component[str, LinkAttrs]")} provides type " + f"{i('Type Hints')}: {Code('Component[str, LinkAttrs]')} provides type " f"safety:", List( Item( - f"{i("str")}: Enforces that all children of the component " + f"{i('str')}: Enforces that all children of the component " "must be strings" ), Item( - f"{i("LinkAttrs")}: Ensures the required to attribute is " + f"{i('LinkAttrs')}: Ensures the required to attribute is " "present" ), ), ), Item( - f"{i("Attributes")}: {Code("LinkAttrs")} inherits from " - f"{Code("Attrs")}, which is a {Code("TypedDict")} " + f"{i('Attributes')}: {Code('LinkAttrs')} inherits from " + f"{Code('Attrs')}, which is a {Code('TypedDict')} " f"(a dictionary with defined types for its keys)", ), ), @@ -115,14 +115,14 @@ def render(self): "The current definition doesn't strictly enforce a single child. " "This means you could technically pass multiple strings " f"({Code('Link("a", "b")')}). To create a stricter component, inherit " - f"from {Code("ComponentStrict")}: This subclass of Component allows for " + f"from {Code('ComponentStrict')}: This subclass of Component allows for " "finer control over children. More about this in the next section." ), H2("Strict Components"), Paragraph( "Strict components offer more precise control over the types and " "structures of their children compared to regular components. Let's " - f"illustrate this with a simplified {Code("Table")} component:" + f"illustrate this with a simplified {Code('Table')} component:" ), CodeBlock( """ @@ -153,29 +153,29 @@ def render(self) -> table: b("Explanation"), List( Item( - f"{i("Strictness")}: The {Code("ComponentStrict")} class allows you to " + f"{i('Strictness')}: The {Code('ComponentStrict')} class allows you to " "enforce the exact types and order of children." ), Item( - f"{i("Table Structure")}:", + f"{i('Table Structure')}:", List( Item( - f"{Code("Table")}: Expects precisely two children: a " - f"{Code("TableHead")} followed by a {Code("TableBody")}." + f"{Code('Table')}: Expects precisely two children: a " + f"{Code('TableHead')} followed by a {Code('TableBody')}." ), Item( - f"{Code("TableHead")}: Accepts only a single {Code("tr")} " + f"{Code('TableHead')}: Accepts only a single {Code('tr')} " "(table row) element as its child." ), Item( - f"{Code("TableBody")}: Accepts a variable number of " - f"{Code("tr")} elements as children." + f"{Code('TableBody')}: Accepts a variable number of " + f"{Code('tr')} elements as children." ), ), ), Item( - f"{i("Type Hints")}: The {Code("*tuple[tr, ...]")} syntax indicates " - f"that {Code("TableBody")} accepts zero or more tr elements." + f"{i('Type Hints')}: The {Code('*tuple[tr, ...]')} syntax indicates " + f"that {Code('TableBody')} accepts zero or more tr elements." ), ), b("Valid Usage (Passes Type Checking)"), @@ -191,18 +191,18 @@ def render(self) -> table: b("Key Benefits"), List( Item( - f"{i("Enforce Structure")}: Prevent incorrect usage that could break " + f"{i('Enforce Structure')}: Prevent incorrect usage that could break " "your component's layout or functionality." ), Item( - f"{i("Type Safety")}: Static type checkers ensure you're building " + f"{i('Type Safety')}: Static type checkers ensure you're building " "valid component hierarchies." ), ), H2("Attributes"), Paragraph( "To ensure type safety and clarity, define your component attributes using " - f"a subclass of the {Code("Attrs")} class. Here's how:" + f"a subclass of the {Code('Attrs')} class. Here's how:" ), CodeBlock( """ @@ -216,13 +216,13 @@ class PersonAttrs(Attrs): """, language="python", ), - b(f"Understanding {Code("Attrs")} and {Code("TypedDict")}"), + b(f"Understanding {Code('Attrs')} and {Code('TypedDict')}"), List( Item( - f"The {Code("Attrs")} class is built upon Python's {Code("TypedDict")} " - f"concept (see {Link( - "PEP-589", to="https://peps.python.org/pep-0589/" - )}) for details). This provides type hints for dictionary-like data " + f"The {Code('Attrs')} class is built upon Python's {Code('TypedDict')} " + f"concept (see { + Link('PEP-589', to='https://peps.python.org/pep-0589/') + }) for details). This provides type hints for dictionary-like data " "structures." ), ), @@ -230,7 +230,7 @@ class PersonAttrs(Attrs): Paragraph( "In the above case, all attributes except for {Code('is_active')} are " "required. If you want to make all attributes NOT required by default, " - f"you can pass the {Code("total=False")} keyword argument to the class " + f"you can pass the {Code('total=False')} keyword argument to the class " "definition:" ), CodeBlock( @@ -246,20 +246,20 @@ class PersonAttrs(Attrs, total=False): language="python", ), Paragraph( - f"In this case, all attributes are optional except for the {Code("id")} " + f"In this case, all attributes are optional except for the {Code('id')} " "attribute." ), Message( Title( - f"The {Code("Attrs")} declaration is an information for type checkers" + f"The {Code('Attrs')} declaration is an information for type checkers" ), - f"The {Code("Attrs")} class just provides typing information for static " + f"The {Code('Attrs')} class just provides typing information for static " "type checkers. Your code will work even if you pass key-word arguments to " "components without declaring them first.", ), Paragraph( "All attributes can also subclass from other classes, for example, you can " - f"extend the attributes for the {Code("