mirror of
https://github.com/rio-labs/rio.git
synced 2026-02-09 23:29:30 -06:00
Merge branch 'dev' of ssh://gitlab.com/team-rio/rio into dev
This commit is contained in:
@@ -2,9 +2,9 @@ repos:
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
rev: v0.3.5
|
||||
hooks:
|
||||
- id: ruff
|
||||
args: [ --select, F401, --fix ]
|
||||
- id: ruff-format
|
||||
- id: ruff
|
||||
args: [--select, F401, --fix]
|
||||
- id: ruff-format
|
||||
|
||||
- repo: https://github.com/pycqa/isort
|
||||
rev: 5.13.2
|
||||
@@ -15,3 +15,4 @@ repos:
|
||||
rev: v4.0.0-alpha.8
|
||||
hooks:
|
||||
- id: prettier
|
||||
exclude: '\.md$'
|
||||
|
||||
@@ -16,7 +16,7 @@ apps.
|
||||
🐍 Rio is based **entirely on Python**. You **won't need a single line of HTML, CSS, or
|
||||
JavaScript** to create beautiful, modern apps.
|
||||
|
||||
[Tutorial](https://rio.dev/docs/tutorial-simple-dashboard/1-rio-setup) - [Examples](https://rio.dev/examples) - [Discord](https://discord.gg/7ejXaPwhyH) - [docs](https://rio.dev/docs) - [Source Code](https://gitlab.com/team-rio/rio)
|
||||
[Tutorial](https://rio.dev/docs/tutorial-simple-dashboard/1-rio-setup) - [Examples](https://rio.dev/examples) - [Discord](https://discord.gg/7ejXaPwhyH) - [Docs](https://rio.dev/docs) - [Source Code](https://gitlab.com/team-rio/rio)
|
||||
|
||||
Rio brings React-style components to Python. Pull from a wealth of built-in
|
||||
components and combine them to create your own custom components. Then combine
|
||||
|
||||
@@ -5,7 +5,7 @@ build-backend = "poetry.core.masonry.api"
|
||||
[tool.poetry]
|
||||
license = "LGPL-3.0" # TODO
|
||||
name = "rio-ui"
|
||||
version = "0.5.1"
|
||||
version = "0.5.3"
|
||||
description = "Build modern Websites and Apps just with Python"
|
||||
authors = ["Jakob Pinterits <jakob.pinterits@gmail.com>", "Paul Pinterits"]
|
||||
readme = "README.md"
|
||||
@@ -50,7 +50,7 @@ fastapi = "^0.109.1"
|
||||
fuzzywuzzy = "^0.18.0"
|
||||
gitignore-parser = "^0.1.9"
|
||||
httpx = "^0.25.1"
|
||||
imy = "^0.2.1"
|
||||
imy = "^0.2.2"
|
||||
introspection = "^1.7.13"
|
||||
keyring = "^24.3.0"
|
||||
pillow = "^10.2.0"
|
||||
@@ -62,7 +62,7 @@ python-levenshtein = "^0.23.0"
|
||||
python-multipart = "^0.0.6"
|
||||
pytz = "^2024.1"
|
||||
pywebview = { version = "^4.2.2", optional = true }
|
||||
revel = "0.8.19"
|
||||
revel = "^0.9.0"
|
||||
timer-dict = "^1.0.0"
|
||||
tomlkit = "^0.12.3"
|
||||
typing-extensions = "^4.5.0"
|
||||
@@ -71,6 +71,7 @@ uniserde = "^0.3.12"
|
||||
uvicorn = { extras = ["standard"], version = "^0.23.2" }
|
||||
watchfiles = "^0.21.0"
|
||||
yarl = "^1.9.2"
|
||||
isort = "^5.13.2"
|
||||
|
||||
[tool.poetry.extras]
|
||||
window = ["cefpython3", "platformdirs", "pygobject", "pywebview"]
|
||||
@@ -79,7 +80,6 @@ window = ["cefpython3", "platformdirs", "pygobject", "pywebview"]
|
||||
alt-pytest-asyncio = "^0.7.2"
|
||||
black = "^23.1.0"
|
||||
coverage = "^7.2.2"
|
||||
isort = "^5.12.0"
|
||||
pre-commit = "^3.1.1"
|
||||
pyfakefs = "^5.3.0"
|
||||
pytest = "^7.3.1"
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import logging
|
||||
from typing import Literal
|
||||
|
||||
import imy.package_metadata
|
||||
import introspection
|
||||
import revel
|
||||
from revel import error, fatal, print, success, warning
|
||||
from revel import * # type: ignore
|
||||
|
||||
import rio.snippets
|
||||
|
||||
@@ -17,7 +17,9 @@ revel.GLOBAL_STYLES.add_alias("primary", ["cyan"])
|
||||
revel.GLOBAL_STYLES.add_alias("bg-primary", ["bg-cyan"])
|
||||
|
||||
app = revel.App(
|
||||
summary="An easy to use, app & web framework for Python",
|
||||
nicename="Rio",
|
||||
command_name="rio",
|
||||
summary="An easy to use, web & app framework for Python",
|
||||
details="""
|
||||
Rio is a framework for building reactive apps and websites in Python. It's
|
||||
designed to be easy to use, and to get out of your way as much as possible.
|
||||
@@ -25,6 +27,7 @@ designed to be easy to use, and to get out of your way as much as possible.
|
||||
This is the command line interface for Rio. You can use it to easily create new
|
||||
projects, run them, and more.
|
||||
""",
|
||||
version=imy.package_metadata.get_package_version("rio-ui"),
|
||||
)
|
||||
|
||||
|
||||
@@ -192,7 +195,7 @@ class {class_name}(rio.Component):
|
||||
def build(self) -> rio.Component:
|
||||
return rio.Markdown('''
|
||||
## This is a sample component
|
||||
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
|
||||
''')
|
||||
"""
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import io
|
||||
import re
|
||||
import shutil
|
||||
import string
|
||||
@@ -5,6 +6,7 @@ from pathlib import Path
|
||||
from typing import * # type: ignore
|
||||
|
||||
import introspection
|
||||
import isort
|
||||
import revel
|
||||
from revel import fatal, print, success
|
||||
|
||||
@@ -47,7 +49,7 @@ def write_init_file(fil: IO, snippets: Iterable[rio.snippets.Snippet]) -> None:
|
||||
|
||||
|
||||
def generate_root_init(
|
||||
fil: TextIO,
|
||||
out: TextIO,
|
||||
*,
|
||||
raw_name: str,
|
||||
project_type: Literal["app", "website"],
|
||||
@@ -84,7 +86,9 @@ def generate_root_init(
|
||||
|
||||
# Imports
|
||||
default_theme = rio.Theme.from_color()
|
||||
fil.write(
|
||||
buffer = io.StringIO()
|
||||
|
||||
buffer.write(
|
||||
f"""
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -102,11 +106,12 @@ from . import components as comps
|
||||
try:
|
||||
additional_imports = root_init_snippet.get_section("additional-imports")
|
||||
except KeyError:
|
||||
pass
|
||||
needs_isort = False
|
||||
else:
|
||||
fil.write("\n")
|
||||
fil.write(additional_imports)
|
||||
fil.write("\n\n")
|
||||
buffer.write("\n")
|
||||
buffer.write(additional_imports)
|
||||
buffer.write("\n\n")
|
||||
needs_isort = True
|
||||
|
||||
# Additional code
|
||||
try:
|
||||
@@ -114,12 +119,12 @@ from . import components as comps
|
||||
except KeyError:
|
||||
pass
|
||||
else:
|
||||
fil.write("\n")
|
||||
fil.write(additional_code)
|
||||
fil.write("\n\n")
|
||||
buffer.write("\n")
|
||||
buffer.write(additional_code)
|
||||
buffer.write("\n\n")
|
||||
|
||||
# Theme & App generation
|
||||
fil.write(
|
||||
buffer.write(
|
||||
f"""
|
||||
# Define a theme for Rio to use.
|
||||
#
|
||||
@@ -143,16 +148,24 @@ app = rio.App(
|
||||
|
||||
# Some parameters are optional
|
||||
if on_app_start is not None:
|
||||
fil.write(
|
||||
buffer.write(
|
||||
f" # This function will be called once the app is ready.\n"
|
||||
f" #\n"
|
||||
f" # `rio run` will also call it again each time the app is reloaded.\n"
|
||||
f" on_app_start={on_app_start},\n"
|
||||
)
|
||||
|
||||
fil.write(" theme=theme,\n")
|
||||
fil.write(' assets_dir=Path(__file__).parent / "assets",\n')
|
||||
fil.write(")\n\n")
|
||||
buffer.write(" theme=theme,\n")
|
||||
buffer.write(' assets_dir=Path(__file__).parent / "assets",\n')
|
||||
buffer.write(")\n\n")
|
||||
|
||||
# Due to imports coming from different sources they're often not sorted.
|
||||
# -> Apply `isort`
|
||||
if needs_isort:
|
||||
formatted_code = isort.code(buffer.getvalue())
|
||||
out.write(formatted_code)
|
||||
else:
|
||||
out.write(buffer.getvalue())
|
||||
|
||||
|
||||
def strip_invalid_filename_characters(name: str) -> str:
|
||||
@@ -226,7 +239,9 @@ def write_component_file(
|
||||
Writes the Python file containing a component or page to the given file.
|
||||
"""
|
||||
# Common imports
|
||||
out.write(
|
||||
buffer = io.StringIO()
|
||||
|
||||
buffer.write(
|
||||
f"""from __future__ import annotations
|
||||
|
||||
from dataclasses import KW_ONLY, field
|
||||
@@ -243,13 +258,22 @@ from .. import components as comps
|
||||
try:
|
||||
additional_imports = snip.get_section("additional-imports")
|
||||
except KeyError:
|
||||
pass
|
||||
needs_isort = False
|
||||
else:
|
||||
out.write(additional_imports)
|
||||
out.write("\n")
|
||||
buffer.write(additional_imports)
|
||||
buffer.write("\n")
|
||||
needs_isort = True
|
||||
|
||||
# The component proper
|
||||
out.write(snip.get_section("component"))
|
||||
buffer.write(snip.get_section("component"))
|
||||
|
||||
# Due to imports coming from different sources they're often not sorted.
|
||||
# -> Apply `isort`
|
||||
if needs_isort:
|
||||
formatted_code = isort.code(buffer.getvalue())
|
||||
out.write(formatted_code)
|
||||
else:
|
||||
out.write(buffer.getvalue())
|
||||
|
||||
|
||||
def generate_dependencies_file(project_dir: Path, dependencies: dict[str, str]) -> None:
|
||||
@@ -358,7 +382,7 @@ def create_project(
|
||||
# Generate /project/__init__.py
|
||||
with open(main_module_dir / "__init__.py", "w") as fil:
|
||||
generate_root_init(
|
||||
fil=fil,
|
||||
out=fil,
|
||||
raw_name=raw_name,
|
||||
project_type=type,
|
||||
components=template.component_snippets,
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import typing
|
||||
|
||||
from .auto_form import *
|
||||
from .banner import *
|
||||
from .button import *
|
||||
@@ -55,5 +53,5 @@ from .tooltip import *
|
||||
from .website import *
|
||||
|
||||
assert (
|
||||
Container is not typing.Container
|
||||
Container.__module__ != "typing"
|
||||
), "Looks like somebody imported `typing.Container`, thus accidentally overwriting `rio.Container`. Are you missing an `__all__` in some component?"
|
||||
|
||||
@@ -70,12 +70,12 @@ class Sidebar(component.Component):
|
||||
rio.Text(
|
||||
self.session.app.name,
|
||||
style="heading2",
|
||||
align_x=0,
|
||||
justify='left',
|
||||
),
|
||||
rio.Text(
|
||||
"TODO: Subtext",
|
||||
style="dim",
|
||||
align_x=0,
|
||||
justify='left',
|
||||
),
|
||||
align_y=0,
|
||||
),
|
||||
|
||||
@@ -23,7 +23,6 @@ import rio
|
||||
from .. import event, global_state, inspection
|
||||
from ..dataclass import RioDataclassMeta, class_local_fields, internal_field
|
||||
from ..state_properties import StateBindingMaker, StateProperty
|
||||
from . import fundamental_component
|
||||
|
||||
__all__ = ["Component"]
|
||||
|
||||
@@ -514,6 +513,8 @@ class Component(abc.ABC, metaclass=ComponentMeta):
|
||||
include_self: bool,
|
||||
recurse_into_high_level_components: bool,
|
||||
) -> Iterable[Component]:
|
||||
from . import fundamental_component # Avoid circular import problem
|
||||
|
||||
# Special case the component itself to handle `include_self`
|
||||
if include_self:
|
||||
yield self
|
||||
@@ -538,6 +539,8 @@ class Component(abc.ABC, metaclass=ComponentMeta):
|
||||
"""
|
||||
Iterate over all components in the component tree, with this component as the root.
|
||||
"""
|
||||
from . import fundamental_component # Avoid circular import problem
|
||||
|
||||
yield self
|
||||
|
||||
if isinstance(self, fundamental_component.FundamentalComponent):
|
||||
|
||||
@@ -10,7 +10,10 @@ import rio
|
||||
from .. import common, inspection
|
||||
from .component import Component
|
||||
|
||||
__all__ = ["FundamentalComponent", "KeyboardFocusableFundamentalComponent"]
|
||||
__all__ = [
|
||||
"FundamentalComponent",
|
||||
"KeyboardFocusableFundamentalComponent",
|
||||
]
|
||||
|
||||
|
||||
JAVASCRIPT_SOURCE_TEMPLATE = """
|
||||
|
||||
@@ -102,7 +102,7 @@ class LabeledColumn(Component):
|
||||
def build(self) -> Component:
|
||||
rows = [
|
||||
[
|
||||
rio.Text(label, align_x=1),
|
||||
rio.Text(label, justify='right'),
|
||||
child,
|
||||
]
|
||||
for label, child in self.content.items()
|
||||
|
||||
@@ -195,7 +195,7 @@ class SimpleListItem(Component):
|
||||
text_children = [
|
||||
rio.Text(
|
||||
self.text,
|
||||
align_x=0,
|
||||
justify='left',
|
||||
)
|
||||
]
|
||||
|
||||
@@ -205,7 +205,7 @@ class SimpleListItem(Component):
|
||||
self.secondary_text,
|
||||
wrap=True,
|
||||
style="dim",
|
||||
align_x=0,
|
||||
justify='left',
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@@ -106,7 +106,7 @@ class ComponentDetails(rio.Component):
|
||||
rio.Text(
|
||||
text,
|
||||
style="dim" if is_label else "text",
|
||||
align_x=1 if is_label else 0,
|
||||
justify='right' if is_label else 'left',
|
||||
),
|
||||
row_index,
|
||||
column_index,
|
||||
@@ -126,7 +126,7 @@ class ComponentDetails(rio.Component):
|
||||
rio.Text(
|
||||
target.key,
|
||||
style="dim",
|
||||
align_x=0,
|
||||
justify='left',
|
||||
),
|
||||
]
|
||||
|
||||
@@ -160,7 +160,7 @@ class ComponentDetails(rio.Component):
|
||||
rio.Text(
|
||||
f"{file} line {line}",
|
||||
style="dim",
|
||||
align_x=0,
|
||||
justify='left',
|
||||
),
|
||||
row_index,
|
||||
0,
|
||||
@@ -227,8 +227,8 @@ class ComponentDetails(rio.Component):
|
||||
row_index += 1
|
||||
|
||||
# Header
|
||||
result.add(rio.Text("width", style="dim", align_x=0), row_index, 1)
|
||||
result.add(rio.Text("height", style="dim", align_x=0), row_index, 2)
|
||||
result.add(rio.Text("width", style="dim", justify='left'), row_index, 1)
|
||||
result.add(rio.Text("height", style="dim", justify='left'), row_index, 2)
|
||||
row_index += 1
|
||||
|
||||
# The size as specified in Python
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
from typing import * # type: ignore
|
||||
|
||||
import rio
|
||||
import rio.components.class_container
|
||||
import rio.components.debugger_connector
|
||||
|
||||
from . import (
|
||||
deploy_page,
|
||||
|
||||
@@ -8,7 +8,7 @@ class DeployPage(rio.Component):
|
||||
"Deploy",
|
||||
style="heading2",
|
||||
margin=1,
|
||||
align_x=0,
|
||||
justify='left',
|
||||
),
|
||||
rio.Column(
|
||||
rio.Icon(
|
||||
|
||||
@@ -8,7 +8,7 @@ class DocsPage(rio.Component):
|
||||
"Documentation",
|
||||
style="heading2",
|
||||
margin=1,
|
||||
align_x=0,
|
||||
justify='left',
|
||||
),
|
||||
rio.Column(
|
||||
rio.Text(
|
||||
|
||||
@@ -206,7 +206,13 @@ class IconsPage(rio.Component):
|
||||
children = []
|
||||
|
||||
# Heading
|
||||
children.append(rio.Text("Configure", style="heading3", align_x=0))
|
||||
children.append(
|
||||
rio.Text(
|
||||
"Configure",
|
||||
style="heading3",
|
||||
justify='left',
|
||||
)
|
||||
)
|
||||
|
||||
# Fill
|
||||
children.append(
|
||||
@@ -279,7 +285,7 @@ class IconsPage(rio.Component):
|
||||
rio.Text(
|
||||
"Example Code",
|
||||
style="heading3",
|
||||
align_x=0,
|
||||
justify='left',
|
||||
margin_top=1,
|
||||
)
|
||||
)
|
||||
|
||||
@@ -36,13 +36,13 @@ class ProjectPage(rio.Component):
|
||||
rio.Text(
|
||||
project_name,
|
||||
style="heading2",
|
||||
align_x=0,
|
||||
justify='left',
|
||||
),
|
||||
rio.Text(
|
||||
str(self.project.project_directory),
|
||||
style="dim",
|
||||
margin_bottom=2,
|
||||
align_x=0,
|
||||
justify='left',
|
||||
),
|
||||
rio.Text(
|
||||
"To launch your project, Rio needs to know the name of your python module and in which variable you've stored your app. You can configure those here.",
|
||||
|
||||
@@ -201,7 +201,7 @@ class PalettePicker(rio.Component): #
|
||||
fill=palette.foreground,
|
||||
),
|
||||
selectable=False,
|
||||
align_x=0,
|
||||
justify='left',
|
||||
),
|
||||
rio.Text(
|
||||
f"#{palette.background.hex}",
|
||||
@@ -209,7 +209,7 @@ class PalettePicker(rio.Component): #
|
||||
font_size=1,
|
||||
fill=palette.foreground.replace(opacity=0.5),
|
||||
),
|
||||
align_x=0,
|
||||
justify='left',
|
||||
),
|
||||
spacing=0.2,
|
||||
margin_x=1,
|
||||
@@ -346,7 +346,7 @@ class ThemePickerPage(rio.Component):
|
||||
# "Theme Colors",
|
||||
# style="heading3",
|
||||
# margin_bottom=1,
|
||||
# align_x=0,
|
||||
# justify='left',
|
||||
# ),
|
||||
PalettePicker(
|
||||
shared_open_key=self.bind().shared_open_key,
|
||||
@@ -419,7 +419,7 @@ class ThemePickerPage(rio.Component):
|
||||
style="heading3",
|
||||
margin_top=1,
|
||||
margin_bottom=1,
|
||||
align_x=0,
|
||||
justify='left',
|
||||
),
|
||||
radius_sliders,
|
||||
# Theme Variants
|
||||
@@ -428,7 +428,7 @@ class ThemePickerPage(rio.Component):
|
||||
style="heading3",
|
||||
margin_top=1,
|
||||
margin_bottom=1,
|
||||
align_x=0,
|
||||
justify='left',
|
||||
),
|
||||
rio.Row(
|
||||
rio.Spacer(),
|
||||
@@ -451,7 +451,7 @@ class ThemePickerPage(rio.Component):
|
||||
style="heading3",
|
||||
margin_top=1,
|
||||
# margin_bottom=1, Not used for now, since markdown has an oddly large margin anyway
|
||||
align_x=0,
|
||||
justify='left',
|
||||
),
|
||||
rio.Markdown(
|
||||
f"""
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from typing import * # type: ignore
|
||||
|
||||
import rio
|
||||
import rio.components.component_tree
|
||||
|
||||
from . import component_details
|
||||
|
||||
@@ -19,7 +19,7 @@ class TreePage(rio.Component):
|
||||
"Component Tree",
|
||||
style="heading2",
|
||||
margin=margin,
|
||||
align_x=0,
|
||||
justify="left",
|
||||
),
|
||||
rio.components.component_tree.ComponentTree(
|
||||
width=10,
|
||||
|
||||
@@ -31,6 +31,7 @@ AvailableTemplatesLiteral: TypeAlias = Literal[
|
||||
"Empty",
|
||||
# Sort the remainder alphabetically
|
||||
"AI Chatbot",
|
||||
"Simple CRUD",
|
||||
"Simple Dashboard",
|
||||
"Tic-Tac-Toe",
|
||||
]
|
||||
|
||||
@@ -19,19 +19,19 @@ class AboutMe(rio.Component):
|
||||
rio.Text(
|
||||
"Jane Doe",
|
||||
style="heading1",
|
||||
align_x=0,
|
||||
justify='left',
|
||||
),
|
||||
rio.Text(
|
||||
"Data Analyst",
|
||||
align_x=0,
|
||||
justify='left',
|
||||
),
|
||||
rio.Text(
|
||||
"AI Researcher",
|
||||
align_x=0,
|
||||
justify='left',
|
||||
),
|
||||
rio.Text(
|
||||
"Python Developer",
|
||||
align_x=0,
|
||||
justify='left',
|
||||
),
|
||||
spacing=0.5,
|
||||
align_y=0,
|
||||
|
||||
@@ -33,7 +33,7 @@ class Contact(rio.Component):
|
||||
image,
|
||||
rio.Text(
|
||||
self.text,
|
||||
align_x=0,
|
||||
justify='left',
|
||||
width="grow",
|
||||
),
|
||||
width="grow",
|
||||
|
||||
@@ -30,20 +30,20 @@ class HistoryItem(rio.Component):
|
||||
rio.Column(
|
||||
rio.Text(
|
||||
f"{self.from_string} - {self.to_string}",
|
||||
align_x=0,
|
||||
justify='left',
|
||||
style=rio.TextStyle(fill=rio.Color.GREY),
|
||||
),
|
||||
rio.Text(
|
||||
f"{self.title} at {self.organization}",
|
||||
style="heading3",
|
||||
align_x=0,
|
||||
justify='left',
|
||||
),
|
||||
rio.Revealer(
|
||||
None,
|
||||
rio.Text(
|
||||
self.details,
|
||||
wrap=True,
|
||||
align_x=0,
|
||||
justify='left',
|
||||
),
|
||||
is_open=self.is_open,
|
||||
),
|
||||
|
||||
@@ -14,7 +14,7 @@ class SkillBars(rio.Component):
|
||||
rio.Column(
|
||||
rio.Text(
|
||||
name,
|
||||
align_x=0,
|
||||
justify='left',
|
||||
),
|
||||
rio.ProgressBar(
|
||||
level / 10,
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
# <additional-imports>
|
||||
import openai
|
||||
|
||||
import rio
|
||||
|
||||
# </additional-imports>
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"level": "intermediate",
|
||||
"summary": "TODO"
|
||||
}
|
||||
Reference in New Issue
Block a user