├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── actions │ ├── setup-npm │ │ └── action.yaml │ └── setup-python │ │ └── action.yaml ├── dependabot.yml └── workflows │ ├── lint.yaml │ ├── release.yaml │ └── test.yaml ├── .gitignore ├── .pre-commit-config.yaml ├── .python-version ├── LICENSE ├── Makefile ├── README.md ├── README_CN.md ├── docs ├── .dockerignore ├── .gitignore ├── .python-version ├── Dockerfile ├── README.md ├── docs-ui │ ├── .eslintrc.cjs │ ├── .gitignore │ ├── .prettierrc │ ├── README.md │ ├── components.json │ ├── index.html │ ├── package.json │ ├── pnpm-lock.yaml │ ├── postcss.config.js │ ├── public │ │ └── vite.svg │ ├── src │ │ ├── App.tsx │ │ ├── component-resolver.tsx │ │ ├── components │ │ │ ├── code-block.tsx │ │ │ └── markdown.tsx │ │ ├── globals.css │ │ ├── lib │ │ │ └── utils.ts │ │ ├── main.tsx │ │ └── vite-env.d.ts │ ├── tailwind.config.js │ ├── tsconfig.json │ ├── tsconfig.node.json │ └── vite.config.ts ├── pyproject.toml ├── requirements-dev.lock ├── requirements.lock ├── requirements.txt ├── src │ └── documentation │ │ ├── __init__.py │ │ ├── app │ │ ├── __init__.py │ │ ├── group__docs │ │ │ ├── __init__.py │ │ │ ├── actions │ │ │ │ ├── __init__.py │ │ │ │ └── dynamic__action_type │ │ │ │ │ ├── __init__.py │ │ │ │ │ └── page.py │ │ │ ├── component.py │ │ │ ├── components │ │ │ │ ├── __init__.py │ │ │ │ ├── data_grid │ │ │ │ │ ├── __init__.py │ │ │ │ │ └── route.py │ │ │ │ ├── deferred_fetch │ │ │ │ │ ├── __init__.py │ │ │ │ │ └── route.py │ │ │ │ ├── dynamic__component_type │ │ │ │ │ ├── __init__.py │ │ │ │ │ └── page.py │ │ │ │ └── form │ │ │ │ │ ├── __init__.py │ │ │ │ │ └── route.py │ │ │ ├── docs │ │ │ │ ├── __init__.py │ │ │ │ └── dynamic__content_name │ │ │ │ │ ├── __init__.py │ │ │ │ │ └── page.py │ │ │ ├── layout.py │ │ │ └── learn │ │ │ │ ├── __init__.py │ │ │ │ └── dynamic__content_name │ │ │ │ ├── __init__.py │ │ │ │ └── page.py │ │ ├── layout.py │ │ └── page.py │ │ ├── components.py │ │ ├── config.py │ │ ├── content │ │ ├── __init__.py │ │ ├── custom-component.md │ │ ├── form.md │ │ ├── installation.md │ │ ├── introduction.md │ │ ├── project-structure.md │ │ ├── routing.md │ │ ├── sitemap.md │ │ ├── tailwindcss.md │ │ └── tutorial.md │ │ └── main.py └── vercel.json ├── examples └── todo │ ├── .gitignore │ ├── .python-version │ ├── Makefile │ ├── README.md │ ├── pyproject.toml │ ├── requirements-dev.lock │ ├── requirements.lock │ └── src │ └── todo │ ├── __init__.py │ ├── app │ ├── __init__.py │ ├── layout.py │ ├── page.py │ └── route.py │ ├── main.py │ └── storage.py ├── package.json ├── pnpm-lock.yaml ├── pnpm-workspace.yaml ├── pyproject.toml ├── requirements-dev.lock ├── requirements.lock ├── src ├── npm-flect-prebuilt │ ├── .eslintrc.cjs │ ├── .gitignore │ ├── .prettierrc │ ├── README.md │ ├── components.json │ ├── index.html │ ├── package.json │ ├── postcss.config.js │ ├── public │ │ └── vite.svg │ ├── src │ │ ├── App.tsx │ │ ├── globals.css │ │ ├── lib │ │ │ └── utils.ts │ │ ├── main.tsx │ │ └── vite-env.d.ts │ ├── tailwind.config.js │ ├── tsconfig.json │ ├── tsconfig.node.json │ └── vite.config.ts ├── npm-flect │ ├── .eslintrc.cjs │ ├── .gitignore │ ├── .prettierrc │ ├── README.md │ ├── components.json │ ├── package.json │ ├── pnpm-lock.yaml │ ├── postcss.config.js │ ├── public │ │ └── vite.svg │ ├── src │ │ ├── application.tsx │ │ ├── components │ │ │ ├── action-resolver.tsx │ │ │ ├── component-resolver.tsx │ │ │ ├── flect │ │ │ │ ├── any-component.tsx │ │ │ │ ├── avatar.tsx │ │ │ │ ├── button.tsx │ │ │ │ ├── code-block.tsx │ │ │ │ ├── container.tsx │ │ │ │ ├── copy-button.tsx │ │ │ │ ├── custom.tsx │ │ │ │ ├── data-grid.tsx │ │ │ │ ├── deferred-fetch.tsx │ │ │ │ ├── dialog.tsx │ │ │ │ ├── display.tsx │ │ │ │ ├── form.tsx │ │ │ │ ├── heading.tsx │ │ │ │ ├── link.tsx │ │ │ │ ├── link.types.ts │ │ │ │ ├── markdown.tsx │ │ │ │ ├── nav-link.tsx │ │ │ │ ├── outlet.tsx │ │ │ │ ├── paragraph.tsx │ │ │ │ ├── table.tsx │ │ │ │ └── text.tsx │ │ │ ├── index.ts │ │ │ ├── routing.tsx │ │ │ └── ui │ │ │ │ ├── avatar.tsx │ │ │ │ ├── button.tsx │ │ │ │ ├── checkbox.tsx │ │ │ │ ├── command.tsx │ │ │ │ ├── dialog.tsx │ │ │ │ ├── form.tsx │ │ │ │ ├── input.tsx │ │ │ │ ├── label.tsx │ │ │ │ ├── popover.tsx │ │ │ │ ├── select.tsx │ │ │ │ ├── sonner.tsx │ │ │ │ ├── table.tsx │ │ │ │ ├── textarea.tsx │ │ │ │ └── tooltip.tsx │ │ ├── contexts │ │ │ ├── action-resolver.tsx │ │ │ ├── component-resolver.tsx │ │ │ └── config.tsx │ │ ├── globals.css │ │ ├── hooks │ │ │ ├── use-action.ts │ │ │ └── use-dispatch-action-listen.ts │ │ ├── index.ts │ │ ├── lib │ │ │ ├── actions.ts │ │ │ ├── ajv-resolver.ts │ │ │ └── utils.ts │ │ ├── types.d.ts │ │ └── vite-env.d.ts │ ├── tailwind.config.js │ ├── tsconfig.json │ ├── tsconfig.node.json │ └── vite.config.ts └── python-flect │ ├── src │ └── flect │ │ ├── __init__.py │ │ ├── actions.py │ │ ├── application.py │ │ ├── component │ │ ├── __init__.py │ │ ├── components.py │ │ ├── data_grid.py │ │ ├── display.py │ │ └── form.py │ │ ├── config.py │ │ ├── constants.py │ │ ├── head.py │ │ ├── py.typed │ │ ├── render.py │ │ ├── response.py │ │ ├── routing │ │ ├── __init__.py │ │ ├── client.py │ │ ├── server.py │ │ └── sort.py │ │ ├── sitemap.py │ │ ├── types.py │ │ ├── utils.py │ │ └── version.py │ └── tests │ ├── __init__.py │ ├── app │ ├── __init__.py │ ├── layout.py │ ├── page.py │ ├── segment1 │ │ ├── __init__.py │ │ ├── dynamic__segment_id │ │ │ ├── __init__.py │ │ │ ├── page.py │ │ │ └── route.py │ │ ├── group__segment2 │ │ │ ├── __init__.py │ │ │ ├── layout.py │ │ │ └── segment3 │ │ │ │ ├── __init__.py │ │ │ │ ├── page.py │ │ │ │ └── route.py │ │ ├── layout.py │ │ ├── page.py │ │ └── segment2 │ │ │ ├── __init__.py │ │ │ ├── page.py │ │ │ └── route.py │ └── utils.py │ ├── conftest.py │ ├── test_head.py │ ├── test_render.py │ ├── test_routing │ ├── __init__.py │ ├── test_client.py │ ├── test_server.py │ └── test_sort.py │ ├── test_sitemap.py │ └── test_utils.py └── tsconfig.json /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | --- 8 | 9 | **Describe the bug** 10 | A clear and concise description of what the bug is. 11 | 12 | **To Reproduce** 13 | Steps to reproduce the behavior: 14 | 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | 28 | - OS: [e.g. iOS] 29 | - Browser [e.g. chrome, safari] 30 | - Version [e.g. 22] 31 | 32 | **Smartphone (please complete the following information):** 33 | 34 | - Device: [e.g. iPhone6] 35 | - OS: [e.g. iOS8.1] 36 | - Browser [e.g. stock browser, safari] 37 | - Version [e.g. 22] 38 | 39 | **Additional context** 40 | Add any other context about the problem here. 41 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | --- 8 | 9 | **Is your feature request related to a problem? Please describe.** 10 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 11 | 12 | **Describe the solution you'd like** 13 | A clear and concise description of what you want to happen. 14 | 15 | **Describe alternatives you've considered** 16 | A clear and concise description of any alternative solutions or features you've considered. 17 | 18 | **Additional context** 19 | Add any other context or screenshots about the feature request here. 20 | -------------------------------------------------------------------------------- /.github/actions/setup-npm/action.yaml: -------------------------------------------------------------------------------- 1 | name: 'Setup NPM' 2 | runs: 3 | using: 'composite' 4 | steps: 5 | - uses: pnpm/action-setup@v3 6 | with: 7 | version: 8 8 | - name: Install dependencies 9 | shell: bash 10 | run: pnpm install 11 | -------------------------------------------------------------------------------- /.github/actions/setup-python/action.yaml: -------------------------------------------------------------------------------- 1 | name: 'Setup Python' 2 | runs: 3 | using: 'composite' 4 | steps: 5 | - uses: actions/setup-python@v5 6 | with: 7 | python-version: '3.11' 8 | - name: Install dependencies 9 | shell: bash 10 | run: curl -sSf https://rye-up.com/get | RYE_NO_AUTO_INSTALL=1 RYE_INSTALL_OPTION="--yes" bash 11 | - name: Add shims 12 | shell: bash 13 | run: echo "$HOME/.rye/shims" >> $GITHUB_PATH 14 | - name: Install 15 | shell: bash 16 | run: make install 17 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # Keep GitHub Actions up to date with GitHub's Dependabot... 2 | # https://docs.github.com/en/code-security/dependabot/working-with-dependabot/keeping-your-actions-up-to-date-with-dependabot 3 | # https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file#package-ecosystem 4 | version: 2 5 | updates: 6 | - package-ecosystem: github-actions 7 | directory: / 8 | groups: 9 | github-actions: 10 | patterns: 11 | - '*' # Group all Actions updates into a single larger pull request 12 | schedule: 13 | interval: monthly 14 | -------------------------------------------------------------------------------- /.github/workflows/lint.yaml: -------------------------------------------------------------------------------- 1 | name: lint 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | lint: 7 | runs-on: ubuntu-latest 8 | 9 | steps: 10 | - uses: actions/checkout@v4 11 | - uses: ./.github/actions/setup-python 12 | - uses: ./.github/actions/setup-npm 13 | 14 | - run: pre-commit run --show-diff-on-failure --color=always --all-files 15 | -------------------------------------------------------------------------------- /.github/workflows/release.yaml: -------------------------------------------------------------------------------- 1 | name: release 2 | on: 3 | push: 4 | tags: 5 | - '[0-9]+.[0-9]+.[0-9]+' 6 | workflow_dispatch: 7 | inputs: 8 | version: 9 | type: string 10 | description: 'Release version' 11 | required: true 12 | 13 | jobs: 14 | release: 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v4 19 | - uses: ./.github/actions/setup-python 20 | - uses: ./.github/actions/setup-npm 21 | 22 | - run: pnpm build 23 | - run: rye build 24 | - run: rye publish --token ${{ secrets.PYPI_TOKEN }} -y 25 | -------------------------------------------------------------------------------- /.github/workflows/test.yaml: -------------------------------------------------------------------------------- 1 | name: test 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | test: 7 | name: test ${{ matrix.python-version }} on ${{ matrix.os }} 8 | strategy: 9 | fail-fast: false 10 | matrix: 11 | os: [ubuntu, macos] 12 | python-version: ['3.9', '3.10', '3.11', '3.12'] 13 | 14 | runs-on: ${{ matrix.os }}-latest 15 | 16 | env: 17 | PYTHON: ${{ matrix.python-version }} 18 | OS: ${{ matrix.os }} 19 | 20 | steps: 21 | - uses: actions/checkout@v4 22 | 23 | - name: set up python 24 | uses: actions/setup-python@v5 25 | with: 26 | python-version: ${{ matrix.python-version }} 27 | 28 | - run: sed '/-e/d' requirements-dev.lock > requirements.txt 29 | - run: pip install -r requirements.txt 30 | - run: pip install -e . 31 | 32 | - run: export PYTHONPATH=docs/src && coverage run -m pytest 33 | # display coverage and fail if it's below 80%, which shouldn't happen 34 | - run: coverage report --fail-under=80 35 | 36 | - run: coverage xml 37 | 38 | - uses: codecov/codecov-action@v4 39 | with: 40 | file: ./coverage.xml 41 | env_vars: PYTHON,OS 42 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib64/ 18 | parts/ 19 | sdist/ 20 | var/ 21 | wheels/ 22 | share/python-wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .nox/ 42 | .coverage 43 | .coverage.* 44 | .cache 45 | nosetests.xml 46 | coverage.xml 47 | *.cover 48 | *.py,cover 49 | .hypothesis/ 50 | .pytest_cache/ 51 | cover/ 52 | 53 | # Translations 54 | *.mo 55 | *.pot 56 | 57 | # Django stuff: 58 | *.log 59 | local_settings.py 60 | db.sqlite3 61 | db.sqlite3-journal 62 | 63 | # Flask stuff: 64 | instance/ 65 | .webassets-cache 66 | 67 | # Scrapy stuff: 68 | .scrapy 69 | 70 | # Sphinx documentation 71 | docs/_build/ 72 | 73 | # PyBuilder 74 | .pybuilder/ 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | # For a library or package, you might want to ignore these files since the code is 86 | # intended to run in multiple environments; otherwise, check them in: 87 | # .python-version 88 | 89 | # pipenv 90 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 91 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 92 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 93 | # install all needed dependencies. 94 | #Pipfile.lock 95 | 96 | # poetry 97 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 98 | # This is especially recommended for binary packages to ensure reproducibility, and is more 99 | # commonly ignored for libraries. 100 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 101 | #poetry.lock 102 | 103 | # pdm 104 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 105 | #pdm.lock 106 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 107 | # in version control. 108 | # https://pdm-project.org/#use-with-ide 109 | .pdm.toml 110 | .pdm-python 111 | .pdm-build/ 112 | 113 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 114 | __pypackages__/ 115 | 116 | # Celery stuff 117 | celerybeat-schedule 118 | celerybeat.pid 119 | 120 | # SageMath parsed files 121 | *.sage.py 122 | 123 | # Environments 124 | .env 125 | .venv 126 | env/ 127 | venv/ 128 | ENV/ 129 | env.bak/ 130 | venv.bak/ 131 | 132 | # Spyder project settings 133 | .spyderproject 134 | .spyproject 135 | 136 | # Rope project settings 137 | .ropeproject 138 | 139 | # mkdocs documentation 140 | /site 141 | 142 | # mypy 143 | .mypy_cache/ 144 | .dmypy.json 145 | dmypy.json 146 | 147 | # Pyre type checker 148 | .pyre/ 149 | 150 | # pytype static type analyzer 151 | .pytype/ 152 | 153 | # Cython debug symbols 154 | cython_debug/ 155 | 156 | # PyCharm 157 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 158 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 159 | # and can be added to the global gitignore or merged into this file. For a more nuclear 160 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 161 | .idea/ 162 | .ruff_cache/ 163 | 164 | .DS_Store 165 | 166 | node_modules/ 167 | .vercel 168 | static/ 169 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/pre-commit/pre-commit-hooks 3 | rev: v4.3.0 4 | hooks: 5 | - id: check-yaml 6 | - id: check-toml 7 | - id: end-of-file-fixer 8 | - id: trailing-whitespace 9 | 10 | - repo: local 11 | hooks: 12 | - id: python-format 13 | name: python-format 14 | types_or: [python] 15 | entry: make format 16 | language: system 17 | pass_filenames: false 18 | 19 | - id: python-typecheck 20 | name: python-typecheck 21 | types_or: [python] 22 | entry: make typecheck 23 | language: system 24 | pass_filenames: false 25 | 26 | - id: js-prettier 27 | name: js-prettier 28 | types_or: [javascript, jsx, ts, tsx, css, json, markdown] 29 | entry: npm run prettier 30 | language: system 31 | -------------------------------------------------------------------------------- /.python-version: -------------------------------------------------------------------------------- 1 | 3.9 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 chaoying 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | path = src/python-flect/src/flect/ 2 | docs_path = docs/src 3 | 4 | .PHONY: install 5 | install: 6 | rye sync 7 | rye install pre-commit 8 | pre-commit install 9 | 10 | .PHONY: format 11 | format: 12 | rye run ruff check --fix-only $(path) docs src/python-flect/ 13 | rye run ruff format $(path) docs src/python-flect/ 14 | 15 | .PHONY: lint 16 | lint: 17 | rye run ruff check $(path) docs src/python-flect/ 18 | rye run ruff format --check $(path) docs src/python-flect/ 19 | 20 | .PHONY: typecheck 21 | typecheck: 22 | rye run pyright 23 | 24 | 25 | .PHONY: test 26 | test: 27 | export PYTHONPATH=$(docs_path) && rye run coverage run -m pytest 28 | 29 | 30 | .PHONY: testcov 31 | testcov: test 32 | coverage html 33 | 34 | 35 | .PHONY: build 36 | build: 37 | npm run build 38 | 39 | .PHONY: dev 40 | dev: 41 | export PYTHONPATH=$(docs_path) && uvicorn docs.src.documentation.main:app --reload --reload-dir . 42 | -------------------------------------------------------------------------------- /docs/.dockerignore: -------------------------------------------------------------------------------- 1 | fly.toml 2 | -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | .vercel 2 | fly.toml 3 | -------------------------------------------------------------------------------- /docs/.python-version: -------------------------------------------------------------------------------- 1 | 3.11.1 2 | -------------------------------------------------------------------------------- /docs/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.11-alpine 2 | 3 | WORKDIR /app 4 | 5 | ENV PYTHONPATH=src 6 | 7 | # Install dependencies 8 | COPY ./requirements.lock . 9 | RUN sed '/-e/d' requirements.lock > requirements.txt 10 | RUN pip install -r requirements.txt 11 | 12 | # Copy source 13 | COPY . . 14 | 15 | CMD uvicorn src.documentation.main:app --host 0.0.0.0 --port 8080 16 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # flect docs 2 | 3 | ## Deploy Your Own 4 | 5 | [![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https://github.com/Chaoyingz/flect/tree/main/docs) 6 | -------------------------------------------------------------------------------- /docs/docs-ui/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { browser: true, es2020: true }, 4 | extends: [ 5 | "eslint:recommended", 6 | "plugin:@typescript-eslint/recommended", 7 | "plugin:react-hooks/recommended", 8 | ], 9 | ignorePatterns: ["dist", ".eslintrc.cjs"], 10 | parser: "@typescript-eslint/parser", 11 | plugins: ["react-refresh"], 12 | rules: { 13 | "react-refresh/only-export-components": [ 14 | "warn", 15 | { allowConstantExport: true }, 16 | ], 17 | }, 18 | }; 19 | -------------------------------------------------------------------------------- /docs/docs-ui/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /docs/docs-ui/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["prettier-plugin-tailwindcss"] 3 | } 4 | -------------------------------------------------------------------------------- /docs/docs-ui/README.md: -------------------------------------------------------------------------------- 1 | # React + TypeScript + Vite 2 | 3 | This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. 4 | 5 | Currently, two official plugins are available: 6 | 7 | - [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh 8 | - [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh 9 | 10 | ## Expanding the ESLint configuration 11 | 12 | If you are developing a production application, we recommend updating the configuration to enable type aware lint rules: 13 | 14 | - Configure the top-level `parserOptions` property like this: 15 | 16 | ```js 17 | export default { 18 | // other rules... 19 | parserOptions: { 20 | ecmaVersion: "latest", 21 | sourceType: "module", 22 | project: ["./tsconfig.json", "./tsconfig.node.json"], 23 | tsconfigRootDir: __dirname, 24 | }, 25 | }; 26 | ``` 27 | 28 | - Replace `plugin:@typescript-eslint/recommended` to `plugin:@typescript-eslint/recommended-type-checked` or `plugin:@typescript-eslint/strict-type-checked` 29 | - Optionally add `plugin:@typescript-eslint/stylistic-type-checked` 30 | - Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and add `plugin:react/recommended` & `plugin:react/jsx-runtime` to the `extends` list 31 | -------------------------------------------------------------------------------- /docs/docs-ui/components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "new-york", 4 | "rsc": false, 5 | "tsx": true, 6 | "tailwind": { 7 | "config": "tailwind.config.js", 8 | "css": "app/globals.css", 9 | "baseColor": "slate", 10 | "cssVariables": true, 11 | "prefix": "" 12 | }, 13 | "aliases": { 14 | "components": "@/components", 15 | "utils": "@/lib/utils" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /docs/docs-ui/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React + TS 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /docs/docs-ui/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "docs-ui", 3 | "description": "Your project description", 4 | "version": "0.1.10", 5 | "main": "dist/index.html", 6 | "type": "module", 7 | "scripts": { 8 | "dev": "vite", 9 | "build": "tsc && vite build", 10 | "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", 11 | "preview": "vite preview" 12 | }, 13 | "dependencies": { 14 | "class-variance-authority": "^0.7.0", 15 | "clsx": "^2.1.0", 16 | "react": "^18.2.0", 17 | "react-dom": "^18.2.0", 18 | "react-markdown": "^9.0.1", 19 | "react-syntax-highlighter": "npm:@fengkx/react-syntax-highlighter@15.6.1", 20 | "remark-gfm": "^4.0.0", 21 | "tailwind-merge": "^2.2.1", 22 | "tailwindcss-animate": "^1.0.7", 23 | "@chaoying/flect": "workspace:*" 24 | }, 25 | "devDependencies": { 26 | "@types/react": "^18.2.64", 27 | "@types/react-dom": "^18.2.21", 28 | "@types/react-syntax-highlighter": "^15.5.11", 29 | "@typescript-eslint/eslint-plugin": "^7.1.1", 30 | "@typescript-eslint/parser": "^7.1.1", 31 | "@vitejs/plugin-react": "^4.2.1", 32 | "autoprefixer": "^10.4.18", 33 | "eslint": "^8.57.0", 34 | "eslint-plugin-react-hooks": "^4.6.0", 35 | "eslint-plugin-react-refresh": "^0.4.5", 36 | "postcss": "^8.4.35", 37 | "prettier": "^3.2.5", 38 | "prettier-plugin-tailwindcss": "^0.5.12", 39 | "tailwindcss": "^3.4.1", 40 | "typescript": "^5.2.2", 41 | "vite": "^5.1.6" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /docs/docs-ui/postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /docs/docs-ui/public/vite.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /docs/docs-ui/src/App.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | Flect, 3 | ActionResolverProvider, 4 | ComponentResolverProvider, 5 | getMetaContent, 6 | } from "@chaoying/flect"; 7 | import { 8 | FlectActionResolver, 9 | FlectComponentResolver, 10 | } from "@chaoying/flect/components"; 11 | import { DocsUIComponentResolver } from "@/component-resolver"; 12 | 13 | function App() { 14 | return ( 15 | 16 | 17 | 18 | 24 | 25 | 26 | 27 | ); 28 | } 29 | 30 | export default App; 31 | -------------------------------------------------------------------------------- /docs/docs-ui/src/component-resolver.tsx: -------------------------------------------------------------------------------- 1 | import { ComponentResolver } from "@chaoying/flect"; 2 | import { Markdown, MarkdownProps } from "@/components/markdown.tsx"; 3 | 4 | type AnyComponentProps = MarkdownProps; 5 | 6 | export const DocsUIComponentResolver: ComponentResolver = ( 7 | props: AnyComponentProps, 8 | ) => { 9 | switch (props.subType) { 10 | case "markdown": 11 | return ; 12 | default: 13 | return null; 14 | } 15 | }; 16 | DocsUIComponentResolver.package = "docs-ui"; 17 | -------------------------------------------------------------------------------- /docs/docs-ui/src/components/code-block.tsx: -------------------------------------------------------------------------------- 1 | import { PrismLight as SyntaxHighlighter } from "react-syntax-highlighter"; 2 | import python from "react-syntax-highlighter/dist/esm/languages/prism/python"; 3 | import tsx from "react-syntax-highlighter/dist/esm/languages/prism/tsx"; 4 | import dracula from "react-syntax-highlighter/dist/esm/styles/prism/dracula"; 5 | import { CopyButton } from "@chaoying/flect/components"; 6 | 7 | import { cn } from "@/lib/utils"; 8 | 9 | SyntaxHighlighter.registerLanguage("python", python); 10 | SyntaxHighlighter.registerLanguage("tsx", tsx); 11 | 12 | export type CodeBlockProps = { 13 | package: "docs-ui"; 14 | type: "code-block"; 15 | subType: "code-block"; 16 | className?: string; 17 | text: string; 18 | language?: string; 19 | }; 20 | 21 | export function CodeBlock(props: CodeBlockProps) { 22 | return ( 23 |
24 | 30 | 37 | {props.text} 38 | 39 |
40 | ); 41 | } 42 | -------------------------------------------------------------------------------- /docs/docs-ui/src/components/markdown.tsx: -------------------------------------------------------------------------------- 1 | import { cn } from "@/lib/utils"; 2 | import ReactMarkdown, { Components } from "react-markdown"; 3 | import remarkGfm from "remark-gfm"; 4 | import { CodeBlock } from "@/components/code-block"; 5 | 6 | export interface MarkdownProps { 7 | package: "docs-ui"; 8 | type: "markdown"; 9 | subType: "markdown"; 10 | className?: string; 11 | text: string; 12 | } 13 | 14 | export function Markdown(props: MarkdownProps) { 15 | const { text, className } = props; 16 | const components: Components = { 17 | code({ children, className }) { 18 | const language = /language-(\w+)/.exec(className || ""); 19 | if (!language) { 20 | return {children}; 21 | } 22 | return ( 23 | 30 | ); 31 | }, 32 | }; 33 | 34 | return ( 35 | 43 | {text} 44 | 45 | ); 46 | } 47 | -------------------------------------------------------------------------------- /docs/docs-ui/src/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /docs/docs-ui/src/lib/utils.ts: -------------------------------------------------------------------------------- 1 | import { type ClassValue, clsx } from "clsx"; 2 | import { twMerge } from "tailwind-merge"; 3 | 4 | export function cn(...inputs: ClassValue[]) { 5 | return twMerge(clsx(inputs)); 6 | } 7 | -------------------------------------------------------------------------------- /docs/docs-ui/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom/client"; 3 | import App from "./App.tsx"; 4 | import "@/globals.css"; 5 | 6 | ReactDOM.createRoot(document.getElementById("root")!).render( 7 | 8 | 9 | , 10 | ); 11 | -------------------------------------------------------------------------------- /docs/docs-ui/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /docs/docs-ui/tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | darkMode: ["class"], 4 | content: [ 5 | "./pages/**/*.{ts,tsx}", 6 | "./components/**/*.{ts,tsx}", 7 | "./app/**/*.{ts,tsx}", 8 | "./src/**/*.{ts,tsx}", 9 | ], 10 | // prefix: 'fp', 11 | theme: { 12 | container: { 13 | center: true, 14 | padding: "2rem", 15 | screens: { 16 | "2xl": "1400px", 17 | }, 18 | }, 19 | extend: { 20 | colors: { 21 | border: "hsl(var(--border))", 22 | input: "hsl(var(--input))", 23 | ring: "hsl(var(--ring))", 24 | background: "hsl(var(--background))", 25 | foreground: "hsl(var(--foreground))", 26 | primary: { 27 | DEFAULT: "hsl(var(--primary))", 28 | foreground: "hsl(var(--primary-foreground))", 29 | }, 30 | secondary: { 31 | DEFAULT: "hsl(var(--secondary))", 32 | foreground: "hsl(var(--secondary-foreground))", 33 | }, 34 | destructive: { 35 | DEFAULT: "hsl(var(--destructive))", 36 | foreground: "hsl(var(--destructive-foreground))", 37 | }, 38 | muted: { 39 | DEFAULT: "hsl(var(--muted))", 40 | foreground: "hsl(var(--muted-foreground))", 41 | }, 42 | accent: { 43 | DEFAULT: "hsl(var(--accent))", 44 | foreground: "hsl(var(--accent-foreground))", 45 | }, 46 | popover: { 47 | DEFAULT: "hsl(var(--popover))", 48 | foreground: "hsl(var(--popover-foreground))", 49 | }, 50 | card: { 51 | DEFAULT: "hsl(var(--card))", 52 | foreground: "hsl(var(--card-foreground))", 53 | }, 54 | }, 55 | borderRadius: { 56 | lg: "var(--radius)", 57 | md: "calc(var(--radius) - 2px)", 58 | sm: "calc(var(--radius) - 4px)", 59 | }, 60 | keyframes: { 61 | "accordion-down": { 62 | from: { height: "0" }, 63 | to: { height: "var(--radix-accordion-content-height)" }, 64 | }, 65 | "accordion-up": { 66 | from: { height: "var(--radix-accordion-content-height)" }, 67 | to: { height: "0" }, 68 | }, 69 | }, 70 | animation: { 71 | "accordion-down": "accordion-down 0.2s ease-out", 72 | "accordion-up": "accordion-up 0.2s ease-out", 73 | }, 74 | }, 75 | }, 76 | safelist: [ 77 | { 78 | pattern: /^(w|max-w|h|max-h)-/, 79 | }, 80 | { 81 | pattern: /^(flex|grid)-/, 82 | }, 83 | { 84 | pattern: /^(m|p)\w?-\d/, 85 | }, 86 | { 87 | pattern: /^gap-\d/, 88 | }, 89 | { 90 | pattern: /^text-(\w{2}|\d{1}\w{2})$/, 91 | }, 92 | { 93 | pattern: /^(font|justify)-/, 94 | }, 95 | "border-b", 96 | "text-center", 97 | "invisible", 98 | "absolute", 99 | "overflow-hidden", 100 | "underline", 101 | "min-h-screen", 102 | ], 103 | corePlugins: { 104 | preflight: false, 105 | }, 106 | plugins: [require("tailwindcss-animate")], 107 | }; 108 | -------------------------------------------------------------------------------- /docs/docs-ui/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "baseUrl": ".", 5 | "paths": { 6 | "@/*": ["./src/*"] 7 | } 8 | }, 9 | "include": ["src"], 10 | "references": [{ "path": "./tsconfig.node.json" }] 11 | } 12 | -------------------------------------------------------------------------------- /docs/docs-ui/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "skipLibCheck": true, 5 | "module": "ESNext", 6 | "moduleResolution": "bundler", 7 | "allowSyntheticDefaultImports": true, 8 | "strict": true 9 | }, 10 | "include": ["vite.config.ts"] 11 | } 12 | -------------------------------------------------------------------------------- /docs/docs-ui/vite.config.ts: -------------------------------------------------------------------------------- 1 | import path from "path"; 2 | import { defineConfig, splitVendorChunkPlugin } from "vite"; 3 | import react from "@vitejs/plugin-react"; 4 | 5 | const serverConfig = { 6 | host: true, 7 | port: 3000, 8 | proxy: { 9 | "/flect": "http://localhost:8000/", 10 | }, 11 | }; 12 | 13 | export default defineConfig({ 14 | plugins: [react(), splitVendorChunkPlugin()], 15 | resolve: { 16 | alias: { 17 | "@": path.resolve(__dirname, "./src"), 18 | }, 19 | }, 20 | server: serverConfig, 21 | preview: serverConfig, 22 | build: { 23 | rollupOptions: { 24 | output: { 25 | entryFileNames: `assets/[name].js`, 26 | chunkFileNames: `assets/[name].js`, 27 | assetFileNames: `assets/[name].[ext]`, 28 | }, 29 | plugins: [], 30 | }, 31 | }, 32 | }); 33 | -------------------------------------------------------------------------------- /docs/pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "docs" 3 | version = "0.1.0" 4 | description = "flect docs" 5 | authors = [ 6 | { name = "Chaoying", email = "chaunceywe@gmail.com" } 7 | ] 8 | dependencies = [ 9 | "flect>=0.2.10", 10 | "pydantic-settings>=2.2.1", 11 | ] 12 | readme = "README.md" 13 | requires-python = ">= 3.9" 14 | 15 | [build-system] 16 | requires = ["hatchling"] 17 | build-backend = "hatchling.build" 18 | 19 | [tool.rye] 20 | managed = true 21 | dev-dependencies = [] 22 | 23 | [tool.hatch.metadata] 24 | allow-direct-references = true 25 | 26 | [tool.hatch.build.targets.wheel] 27 | packages = ["src/docs"] 28 | -------------------------------------------------------------------------------- /docs/requirements-dev.lock: -------------------------------------------------------------------------------- 1 | # generated by rye 2 | # use `rye lock` or `rye sync` to update this lockfile 3 | # 4 | # last locked with the following flags: 5 | # pre: false 6 | # features: [] 7 | # all-features: false 8 | # with-sources: false 9 | 10 | -e file:. 11 | annotated-types==0.6.0 12 | # via pydantic 13 | anyio==4.3.0 14 | # via starlette 15 | click==8.1.7 16 | # via uvicorn 17 | fastapi==0.110.0 18 | # via flect 19 | flect==0.2.10 20 | # via docs 21 | h11==0.14.0 22 | # via uvicorn 23 | idna==3.6 24 | # via anyio 25 | pydantic==2.6.4 26 | # via fastapi 27 | # via flect 28 | # via pydantic-settings 29 | pydantic-core==2.16.3 30 | # via pydantic 31 | pydantic-settings==2.2.1 32 | # via docs 33 | pyromark==0.3.0 34 | # via flect 35 | python-dotenv==1.0.1 36 | # via pydantic-settings 37 | sniffio==1.3.1 38 | # via anyio 39 | starlette==0.36.3 40 | # via fastapi 41 | typing-extensions==4.10.0 42 | # via fastapi 43 | # via pydantic 44 | # via pydantic-core 45 | uvicorn==0.29.0 46 | # via flect 47 | -------------------------------------------------------------------------------- /docs/requirements.lock: -------------------------------------------------------------------------------- 1 | # generated by rye 2 | # use `rye lock` or `rye sync` to update this lockfile 3 | # 4 | # last locked with the following flags: 5 | # pre: false 6 | # features: [] 7 | # all-features: false 8 | # with-sources: false 9 | 10 | -e file:. 11 | annotated-types==0.6.0 12 | # via pydantic 13 | anyio==4.3.0 14 | # via starlette 15 | click==8.1.7 16 | # via uvicorn 17 | fastapi==0.110.0 18 | # via flect 19 | flect==0.2.10 20 | # via docs 21 | h11==0.14.0 22 | # via uvicorn 23 | idna==3.6 24 | # via anyio 25 | pydantic==2.6.4 26 | # via fastapi 27 | # via flect 28 | # via pydantic-settings 29 | pydantic-core==2.16.3 30 | # via pydantic 31 | pydantic-settings==2.2.1 32 | # via docs 33 | pyromark==0.3.0 34 | # via flect 35 | python-dotenv==1.0.1 36 | # via pydantic-settings 37 | sniffio==1.3.1 38 | # via anyio 39 | starlette==0.36.3 40 | # via fastapi 41 | typing-extensions==4.10.0 42 | # via fastapi 43 | # via pydantic 44 | # via pydantic-core 45 | uvicorn==0.29.0 46 | # via flect 47 | -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | flect 2 | pydantic_settings 3 | -------------------------------------------------------------------------------- /docs/src/documentation/__init__.py: -------------------------------------------------------------------------------- 1 | import pathlib 2 | 3 | CONTENT_DIR = pathlib.Path(__file__).parent / "content" 4 | -------------------------------------------------------------------------------- /docs/src/documentation/app/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Chaoyingz/flect/57970b59f98e5ce729494e8ce2986b4eecac2138/docs/src/documentation/app/__init__.py -------------------------------------------------------------------------------- /docs/src/documentation/app/group__docs/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Chaoyingz/flect/57970b59f98e5ce729494e8ce2986b4eecac2138/docs/src/documentation/app/group__docs/__init__.py -------------------------------------------------------------------------------- /docs/src/documentation/app/group__docs/actions/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Chaoyingz/flect/57970b59f98e5ce729494e8ce2986b4eecac2138/docs/src/documentation/app/group__docs/actions/__init__.py -------------------------------------------------------------------------------- /docs/src/documentation/app/group__docs/actions/dynamic__action_type/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Chaoyingz/flect/57970b59f98e5ce729494e8ce2986b4eecac2138/docs/src/documentation/app/group__docs/actions/dynamic__action_type/__init__.py -------------------------------------------------------------------------------- /docs/src/documentation/app/group__docs/component.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | from flect import components as c 4 | from pydantic import BaseModel 5 | from pydantic_core import PydanticUndefined 6 | 7 | 8 | class APIReference(BaseModel): 9 | prop: str 10 | type: str 11 | default: Optional[str] 12 | description: Optional[str] 13 | 14 | 15 | def get_api_reference_section(component: c.AnyComponent) -> c.Container: 16 | props = [] 17 | for field, filed_info in component.model_fields.items(): 18 | if field in ["type", "type"]: 19 | continue 20 | if field == "children": 21 | filed_info.annotation = "flect.components.AnyComponents" 22 | filed_info.default = "[]" 23 | filed_info.description = "The children of the component." 24 | if filed_info.default == PydanticUndefined: 25 | filed_info.default = "None" 26 | filed_info.default = str(filed_info.default) 27 | props.append( 28 | APIReference( 29 | prop=field, 30 | type=str(filed_info.annotation), 31 | default=filed_info.default, 32 | description=filed_info.description, 33 | ) 34 | ) 35 | return c.Container( 36 | tag="section", 37 | children=[ 38 | c.Heading( 39 | level=2, 40 | text="API Reference", 41 | class_name="text-2xl mb-6 border-b pb-2", 42 | ), 43 | c.Table(model=APIReference, datasets=props), 44 | ], 45 | ) 46 | -------------------------------------------------------------------------------- /docs/src/documentation/app/group__docs/components/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Chaoyingz/flect/57970b59f98e5ce729494e8ce2986b4eecac2138/docs/src/documentation/app/group__docs/components/__init__.py -------------------------------------------------------------------------------- /docs/src/documentation/app/group__docs/components/data_grid/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Chaoyingz/flect/57970b59f98e5ce729494e8ce2986b4eecac2138/docs/src/documentation/app/group__docs/components/data_grid/__init__.py -------------------------------------------------------------------------------- /docs/src/documentation/app/group__docs/components/data_grid/route.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import json 3 | 4 | from fastapi.encoders import jsonable_encoder 5 | from flect.actions import Notify 6 | from flect.response import ActionResponse 7 | 8 | from documentation.app.group__docs.components.dynamic__component_type.page import DataGridExampleModel 9 | 10 | 11 | async def post(form: DataGridExampleModel) -> ActionResponse: 12 | await asyncio.sleep(0.1) 13 | return ActionResponse( 14 | action=Notify( 15 | title="You submitted the following values:", 16 | description=json.dumps(jsonable_encoder(form), indent=2), 17 | ) 18 | ) 19 | -------------------------------------------------------------------------------- /docs/src/documentation/app/group__docs/components/deferred_fetch/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Chaoyingz/flect/57970b59f98e5ce729494e8ce2986b4eecac2138/docs/src/documentation/app/group__docs/components/deferred_fetch/__init__.py -------------------------------------------------------------------------------- /docs/src/documentation/app/group__docs/components/deferred_fetch/route.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | 3 | from flect import components as c 4 | from flect.response import PageResponse 5 | 6 | 7 | async def get() -> PageResponse: 8 | await asyncio.sleep(0.1) 9 | return PageResponse( 10 | body=c.Container( 11 | tag="div", 12 | children=[ 13 | c.Heading( 14 | level=1, 15 | text="Hello deferred fetch!", 16 | ), 17 | ], 18 | ) 19 | ) 20 | -------------------------------------------------------------------------------- /docs/src/documentation/app/group__docs/components/dynamic__component_type/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Chaoyingz/flect/57970b59f98e5ce729494e8ce2986b4eecac2138/docs/src/documentation/app/group__docs/components/dynamic__component_type/__init__.py -------------------------------------------------------------------------------- /docs/src/documentation/app/group__docs/components/form/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Chaoyingz/flect/57970b59f98e5ce729494e8ce2986b4eecac2138/docs/src/documentation/app/group__docs/components/form/__init__.py -------------------------------------------------------------------------------- /docs/src/documentation/app/group__docs/components/form/route.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import json 3 | 4 | from fastapi.encoders import jsonable_encoder 5 | from flect.actions import Notify 6 | from flect.response import ActionResponse 7 | 8 | from documentation.app.group__docs.components.dynamic__component_type.page import FormExampleModel 9 | 10 | 11 | async def post(form: FormExampleModel) -> ActionResponse: 12 | await asyncio.sleep(0.1) 13 | return ActionResponse( 14 | action=Notify( 15 | title="You submitted the following values:", 16 | description=json.dumps(jsonable_encoder(form), indent=2), 17 | ) 18 | ) 19 | -------------------------------------------------------------------------------- /docs/src/documentation/app/group__docs/docs/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Chaoyingz/flect/57970b59f98e5ce729494e8ce2986b4eecac2138/docs/src/documentation/app/group__docs/docs/__init__.py -------------------------------------------------------------------------------- /docs/src/documentation/app/group__docs/docs/dynamic__content_name/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Chaoyingz/flect/57970b59f98e5ce729494e8ce2986b4eecac2138/docs/src/documentation/app/group__docs/docs/dynamic__content_name/__init__.py -------------------------------------------------------------------------------- /docs/src/documentation/app/group__docs/docs/dynamic__content_name/page.py: -------------------------------------------------------------------------------- 1 | from typing import Annotated 2 | 3 | from fastapi import Path, Request 4 | from flect import PageResponse 5 | from flect import components as c 6 | from flect.constants import ROOT_ROUTE_PREFIX 7 | from flect.head import Head 8 | from flect.sitemap import Sitemap 9 | 10 | from documentation import CONTENT_DIR 11 | from documentation.app.group__docs.layout import get_docs_pager 12 | from documentation.components import Markdown 13 | 14 | 15 | async def sitemap(dynamic_url: str) -> list[Sitemap]: 16 | return [ 17 | Sitemap( 18 | url=dynamic_url.format(content_name=content_name), 19 | last_modified=None, 20 | change_frequency=None, 21 | priority=None, 22 | ) 23 | for content_name in ["introduction", "installation", "tutorial"] 24 | ] 25 | 26 | 27 | async def page( 28 | request: Request, 29 | content_name: Annotated[str, Path(..., description="The content name to render")], 30 | ) -> PageResponse: 31 | return PageResponse( 32 | head=Head( 33 | title=content_name, 34 | ), 35 | body=c.Container( 36 | tag="div", 37 | children=[ 38 | Markdown.from_file(CONTENT_DIR / f"{content_name}.md"), 39 | get_docs_pager(current_link=request.url.path.replace(ROOT_ROUTE_PREFIX, "")), 40 | ], 41 | ), 42 | ) 43 | -------------------------------------------------------------------------------- /docs/src/documentation/app/group__docs/learn/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Chaoyingz/flect/57970b59f98e5ce729494e8ce2986b4eecac2138/docs/src/documentation/app/group__docs/learn/__init__.py -------------------------------------------------------------------------------- /docs/src/documentation/app/group__docs/learn/dynamic__content_name/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Chaoyingz/flect/57970b59f98e5ce729494e8ce2986b4eecac2138/docs/src/documentation/app/group__docs/learn/dynamic__content_name/__init__.py -------------------------------------------------------------------------------- /docs/src/documentation/app/group__docs/learn/dynamic__content_name/page.py: -------------------------------------------------------------------------------- 1 | from typing import Annotated 2 | 3 | from fastapi import Path, Request 4 | from flect import PageResponse 5 | from flect import components as c 6 | from flect.constants import ROOT_ROUTE_PREFIX 7 | from flect.head import Head 8 | from flect.sitemap import Sitemap 9 | 10 | from documentation import CONTENT_DIR 11 | from documentation.app.group__docs.layout import get_docs_pager 12 | from documentation.components import Markdown 13 | 14 | 15 | async def sitemap(dynamic_url: str) -> list[Sitemap]: 16 | return [ 17 | Sitemap( 18 | url=dynamic_url.format(content_name=content_name), 19 | last_modified=None, 20 | change_frequency=None, 21 | priority=None, 22 | ) 23 | for content_name in ["project-structure", "routing", "form", "custom-component", "tailwindcss", "sitemap"] 24 | ] 25 | 26 | 27 | async def page( 28 | request: Request, 29 | content_name: Annotated[str, Path(..., description="The content name to render")], 30 | ) -> PageResponse: 31 | return PageResponse( 32 | head=Head( 33 | title=f"Learn {content_name}", 34 | ), 35 | body=c.Container( 36 | tag="div", 37 | children=[ 38 | Markdown.from_file(CONTENT_DIR / f"{content_name}.md"), 39 | get_docs_pager(current_link=request.url.path.replace(ROOT_ROUTE_PREFIX, "")), 40 | ], 41 | ), 42 | ) 43 | -------------------------------------------------------------------------------- /docs/src/documentation/app/page.py: -------------------------------------------------------------------------------- 1 | from flect import PageResponse 2 | from flect import components as c 3 | 4 | 5 | async def page() -> PageResponse: 6 | return PageResponse( 7 | body=c.Container( 8 | tag="section", 9 | class_name="container", 10 | children=[ 11 | c.Container( 12 | tag="section", 13 | class_name="flex justify-center py-32 flex-col items-center", 14 | children=[ 15 | c.Heading( 16 | level=1, 17 | text="Turning ideas into web app fast", 18 | class_name="text-center text-5xl font-bold", 19 | ), 20 | c.Text( 21 | text="flect enables you to quickly create full-stack web applications using Python.", 22 | class_name="text-center text-xl mt-8 w-1/2", 23 | ), 24 | c.Container( 25 | tag="div", 26 | class_name="flex gap-4 mt-8", 27 | children=[ 28 | c.Link( 29 | href="/docs/introduction/", 30 | children=[ 31 | c.Button( 32 | children=[c.Text(text="Get Started")], 33 | ) 34 | ], 35 | ), 36 | c.Link( 37 | href="https://github.com/Chaoyingz/flect", 38 | children=[ 39 | c.Button( 40 | children=[c.Text(text="GitHub")], 41 | variant="outline", 42 | ) 43 | ], 44 | ), 45 | ], 46 | ), 47 | ], 48 | ) 49 | ], 50 | ) 51 | ) 52 | -------------------------------------------------------------------------------- /docs/src/documentation/components.py: -------------------------------------------------------------------------------- 1 | import pathlib 2 | from typing import Literal, Optional 3 | 4 | import pyromark 5 | from flect import components as c 6 | from typing_extensions import Self 7 | 8 | 9 | class BaseUIComponent(c.Custom): 10 | package: Literal["docs-ui"] = "docs-ui" 11 | sub_type: Literal["badge"] = "markdown" 12 | 13 | 14 | class Markdown(BaseUIComponent): 15 | class_name: Optional[str] = None 16 | text: str 17 | 18 | def render_to_html(self) -> str: 19 | return f"""\ 20 | {pyromark.markdown(self.text)} 21 | """ 22 | 23 | @classmethod 24 | def from_file(cls, path: pathlib.Path, class_name: Optional[str] = None) -> Self: 25 | try: 26 | return cls(text=path.read_text(), class_name=class_name) 27 | except FileNotFoundError: 28 | return cls(text="Markdown file not found.", class_name=class_name) 29 | -------------------------------------------------------------------------------- /docs/src/documentation/config.py: -------------------------------------------------------------------------------- 1 | from functools import lru_cache 2 | 3 | try: 4 | from pydantic_settings import BaseSettings 5 | except ImportError: 6 | from pydantic import BaseModel as BaseSettings 7 | 8 | 9 | class Settings(BaseSettings): 10 | debug: bool = False 11 | google_measurement_id: str = "" 12 | 13 | 14 | @lru_cache 15 | def get_settings(): 16 | return Settings() 17 | 18 | 19 | settings = get_settings() 20 | -------------------------------------------------------------------------------- /docs/src/documentation/content/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Chaoyingz/flect/57970b59f98e5ce729494e8ce2986b4eecac2138/docs/src/documentation/content/__init__.py -------------------------------------------------------------------------------- /docs/src/documentation/content/form.md: -------------------------------------------------------------------------------- 1 | # Form 2 | 3 | The flect framework allows you to create forms with ease. 4 | 5 | ## Defining Your Form Model 6 | 7 | To define your form model, you can use Python's `Annotated` and `pydantic`'s `BaseModel` and `Field`. Here's an example: 8 | 9 | ```python 10 | from pydantic import BaseModel 11 | from flect.form import Input 12 | 13 | class FormExampleModel(BaseModel): 14 | # Use Annotated to define the field 15 | name: str = Input(placeholder="Enter your username", pattern=r"^[a-zA-Z0-9]+$", min_items=2, max_items=10) 16 | ``` 17 | 18 | In this example, we define a `name` field with a placeholder text "Enter your username". The field accepts alphanumeric characters with a minimum length of 2 and a maximum length of 10. 19 | 20 | ## Using the Form Model 21 | 22 | Once you've defined your form model, you can use it in your form like so: 23 | 24 | ```python 25 | from flect import components as c 26 | from flect import PageResponse 27 | 28 | def page() -> PageResponse: 29 | return PageResponse( 30 | body=c.Container( 31 | tag="div", 32 | children=[ 33 | c.Form( 34 | model=FormExampleModel, 35 | submit_url="/" 36 | ) 37 | ] 38 | ) 39 | ) 40 | ``` 41 | 42 | In this example, we create a `PageResponse` that contains a `Container` with a single child, a `Form` that uses the `FormExampleModel` we defined earlier. The form's `submit_url` is set to the root ("/"). 43 | 44 | ## Adding Server Logic 45 | 46 | After defining the form and its model, you can add server logic to handle the form submission. Here's a simple example: 47 | 48 | ```python 49 | from flect.response import ActionResponse 50 | 51 | async def post(form: FormExampleModel) -> ActionResponse: 52 | # handle the form 53 | ``` 54 | 55 | In this function, we define an asynchronous function `post` that accepts a `FormExampleModel` instance and returns an `ActionResponse`. You can add your form handling logic within this function. 56 | -------------------------------------------------------------------------------- /docs/src/documentation/content/installation.md: -------------------------------------------------------------------------------- 1 | # Installation Guide 2 | 3 | This guide will help you install the necessary dependencies and assist you in setting up your application. 4 | 5 | ## Requirements 6 | 7 | - Python 3.9 or higher 8 | 9 | ## Basic Installation 10 | 11 | If you prefer to start a project from scratch, you can directly install the flect. 12 | 13 | 1. **Install flect** 14 | 15 | To install the flect, run the following command in your terminal: 16 | 17 | ```console 18 | pip install flect 19 | ``` 20 | 21 | ## Installation Using Project Template (Recommended) 22 | 23 | For those who prefer to use a project template to kickstart their application, follow these steps: 24 | 25 | ### Additional Requirements 26 | 27 | - Rye (https://rye-up.com/) for dependency management 28 | - Cookiecutter (https://cookiecutter.readthedocs.io/en/stable/) for project scaffolding 29 | 30 | ### Steps 31 | 32 | 1. **Create Project Structure** 33 | 34 | Use Cookiecutter to create the project structure by running: 35 | 36 | ```console 37 | cookiecutter https://github.com/Chaoyingz/cookiecutter-flect 38 | ``` 39 | 40 | 2. **Navigate to Project Directory** 41 | 42 | Change into your project's directory: 43 | 44 | ```console 45 | cd {project_slug} 46 | ``` 47 | 48 | 3. **Synchronize Dependencies** 49 | 50 | Use Rye to synchronize your project's dependencies: 51 | 52 | ```console 53 | rye sync 54 | ``` 55 | 56 | 4. **Run Your Application** 57 | 58 | Start your application with Uvicorn: 59 | 60 | ```console 61 | make dev 62 | ``` 63 | 64 | 5. **Visit Your Application** 65 | 66 | Open your browser and visit http://127.0.0.1:8000 you will see the following page. 67 | 68 | ![welcome-to-flect](https://github.com/Chaoyingz/flect/assets/32626585/12c0f31b-8030-41b6-8c4b-3efb11e419ca) 69 | 70 | By following these steps, you can either set up your project from scratch or use a pre-defined template to get started quickly. 71 | -------------------------------------------------------------------------------- /docs/src/documentation/content/introduction.md: -------------------------------------------------------------------------------- 1 | ## Introduction 2 | 3 | Welcome to the flect framework documentation! 4 | 5 | ### What is flect? 6 | 7 | flect is a Python framework for building full-stack web applications. It constructs user interfaces by utilizing Pydantic 8 | models in the backend that correspond to the properties of React components in the frontend. This integration enables 9 | quick development of interactive and beautiful UIs using Python. 10 | 11 | The key features are: 12 | 13 | - **Fast development**: Write your entire app with Python, seamlessly integrating backend logic and frontend UI. 14 | - **Easy Form Validation**: Define a single Pydantic model for seamless and consistent form validation across your app, enhancing development speed and reducing potential errors. 15 | - **Client-Side Routing**: Fast, smooth page transitions without reloads. 16 | - **Folder-Based Routing**: Easy route management through folder structure. 17 | - **SEO Friendly**: Supports server-side rendering for better search engine visibility. 18 | 19 | This project is inspired by the [FastUI](https://github.com/pydantic/FastUI) and [Nextjs](https://nextjs.org/) framework. 20 | 21 | ## Why use flect? 22 | 23 | flect enables developers to harness the combined power of Python and JavaScript ecosystems, facilitating the creation of web applications with efficiency and ease: 24 | 25 | > If you're a Python developer — you can build responsive web applications using React without writing a single line of JavaScript, or touching npm. 26 | > 27 | > If you're a frontend developer — you can concentrate on building magical components that are truly reusable, no copy-pasting components for each view. 28 | > 29 | > For everyone — a true separation of concerns, the backend defines the entire application; while the frontend is free to implement just the user interface. 30 | > 31 | > — _From FastUI_ 32 | 33 | ## License 34 | 35 | This project is licensed under the terms of the MIT license. 36 | -------------------------------------------------------------------------------- /docs/src/documentation/content/project-structure.md: -------------------------------------------------------------------------------- 1 | # flect project structure 2 | 3 | This document provides an overview of the project structure for a flect application, with a focus on routing conventions. 4 | 5 | ## Routing Conventions 6 | 7 | Understanding the routing conventions is crucial for navigating through a flect application. Here are the key components and their functions: 8 | 9 | ### Routing Files 10 | 11 | - `layout.py`: This file defines the User Interface (UI) shared across all routes. It sets the common layout for different pages in the application. 12 | 13 | - `page.py`: This file is responsible for the UI unique to a specific route. Each unique page in the application corresponds to a `page.py` file. 14 | 15 | - `route.py`: This file defines the API endpoint for a specific route. 16 | 17 | ### Nested Routes 18 | 19 | Nested routes allow for a hierarchical structure in the application's navigation: 20 | 21 | - `folder`: Represents a route segment. 22 | 23 | - `folder/folder`: Represents a nested route segment. It's a sub-route within a main route segment. 24 | 25 | ### Dynamic Routes 26 | 27 | Dynamic routes allow for more flexible navigation paths: 28 | 29 | - `dynamic__{folder}`: Represents a dynamic route segment. The route can change based on certain conditions or parameters. 30 | 31 | ### Route Groups 32 | 33 | Route groups help organize related routes without affecting the actual routing: 34 | 35 | - `group__{folder}`: Represents a group of related routes. This does not affect the routing but helps in organizing similar routes together. 36 | -------------------------------------------------------------------------------- /docs/src/documentation/content/routing.md: -------------------------------------------------------------------------------- 1 | # Routing 2 | 3 | Routing serves as the cornerstone of every application. This guide will delve into the crucial aspects of web routing and the best practices for managing routes in a Flex application. 4 | 5 | ## Route Segments 6 | 7 | Route segments are represented by folders within a route, each corresponding to a specific segment of a URL path. 8 | 9 | Consider this folder hierarchy: 10 | 11 | ```console 12 | app 13 | ├── layout.py 14 | ├── page.py 15 | └── dashboard 16 | ├── page.py 17 | └── users 18 | └── page.py 19 | ``` 20 | 21 | In this layout, `dashboard` and `users` function as route segments. To access the `users` page within `dashboard`, the URL path would be `/dashboard/users`. 22 | 23 | Each `page.py` file is associated with a distinct URL path, determined by its position in the directory structure: 24 | 25 | - `app/page.py` corresponds to the root URL path (`/`), displaying the default page when visiting the application's base URL. 26 | - `app/dashboard/page.py` is linked to `/dashboard/`, showing the relevant page upon navigation to this path. 27 | - `app/dashboard/users/page.py` maps to `/dashboard/users/`, presenting the appropriate page for this path. 28 | 29 | This setup allows each `page.py` file to represent a unique route, with the folder structure directly reflecting the routing architecture of the application. It simplifies the management and comprehension of your application's routes, making it intuitive and straightforward. 30 | 31 | ## Defining Routing Files 32 | 33 | ### layout.py 34 | 35 | A layout defines a user interface (UI) shared across multiple routes, maintaining its state and interactivity across navigation without needing to re-render. Layouts can also be nested. 36 | 37 | To define a layout: 38 | 39 | ```python 40 | from flect import PageResponse 41 | from flect import components as c 42 | 43 | # The layout function should be named "layout" and take an "outlet" parameter 44 | async def layout(outlet: c.AnyComponent = c.Outlet()) -> PageResponse: 45 | return PageResponse( 46 | body=c.Container( 47 | tag="div", 48 | class_name="flex", 49 | children=[outlet], # the outlet renders the layout's content 50 | ) 51 | ) 52 | ``` 53 | 54 | ### page.py 55 | 56 | A page represents a UI specific to a route, with each distinct page corresponding to a separate `page.py` file. 57 | 58 | To define a page: 59 | 60 | ```python 61 | from flect import PageResponse 62 | from flect import components as c 63 | 64 | async def page() -> PageResponse: 65 | return PageResponse( 66 | body=c.Container( 67 | tag="div", 68 | ) 69 | ) 70 | ``` 71 | 72 | ### routing.py 73 | 74 | The `routing.py` file outlines your application's routes, often used for handling forms. For instance, you can define a POST endpoint to process form submissions as follows: 75 | 76 | ```python 77 | from pydantic import BaseModel 78 | from flect.actions import Notify 79 | from flect.form import Input 80 | from flect.response import ActionResponse 81 | 82 | class FormExampleModel(BaseModel): 83 | username: str = Input( 84 | placeholder="Enter your username", default="", pattern=r"^[a-zA-Z0-9]+$", min_items=2, max_items=10 85 | ) 86 | 87 | async def post(form: FormExampleModel) -> ActionResponse: 88 | return ActionResponse( 89 | action=Notify( 90 | title=f"You submitted username: {form.username}", 91 | ) 92 | ) 93 | ``` 94 | 95 | Here, the route name corresponds to the method name, such as `post` for processing POST requests. 96 | 97 | This approach offers a clear and scalable framework for managing your application's routing. 98 | -------------------------------------------------------------------------------- /docs/src/documentation/content/sitemap.md: -------------------------------------------------------------------------------- 1 | # Sitemap Integration in flect 2 | 3 | Sitemaps play a crucial role in optimizing website visibility for search engines by providing a roadmap of all accessible paths within your application. 4 | 5 | By default, flect automatically parses all the paths in your app to generate a sitemap. For dynamic routes, you have the flexibility to define a function within your page files that generates links for inclusion in the sitemap. 6 | 7 | Here's how you can define a sitemap function for dynamic routes in Python: 8 | 9 | ```python 10 | from flect.sitemap import Sitemap 11 | 12 | async def sitemap(dynamic_url: str) -> list[Sitemap]: 13 | return [ 14 | Sitemap( 15 | url=dynamic_url.format(slug_name=slug_name), 16 | last_modified=None, 17 | change_frequency=None, 18 | priority=None, 19 | ) 20 | for slug_name in ["x", "y", "z"] 21 | ] 22 | ``` 23 | 24 | In this example, `slug_name` represents the variable portion of the dynamic route, and `["x", "y", "z"]` illustrates all possible values for `slug_name`. This approach allows you to easily include dynamic content in your sitemap, ensuring that search engines can discover and index these pages efficiently. 25 | -------------------------------------------------------------------------------- /docs/src/documentation/content/tailwindcss.md: -------------------------------------------------------------------------------- 1 | # TailwindCSS in flect 2 | 3 | flect enables the use of [TailwindCSS](https://tailwindcss.com/) within Python to define styles. This integration is facilitated through TailwindCSS's [safelist](https://tailwindcss.com/docs/content-configuration#safelisting-classes) feature, allowing you to utilize a variety of TailwindCSS class names directly in Python. 4 | 5 | Below is a list of TailwindCSS class names you can use in Python, supported by the safelist configuration: 6 | 7 | ```js 8 | safelist: [ 9 | 'sr-only', 10 | { 11 | pattern: /^(w|max-w|h|max-h)-/, 12 | }, 13 | { 14 | pattern: /^(flex|grid)-/, 15 | }, 16 | { 17 | pattern: /^(m|p)\w?-\d/, 18 | }, 19 | { 20 | pattern: /^gap-\d/, 21 | }, 22 | { 23 | pattern: /^text-(\w{2}|\d{1}\w{2})$/, 24 | }, 25 | { 26 | pattern: /^(font|justify)-/, 27 | }, 28 | 'border-b', 29 | 'text-center', 30 | 'invisible', 31 | 'absolute', 32 | 'overflow-hidden', 33 | 'underline', 34 | 'min-h-screen', 35 | ] 36 | ``` 37 | 38 | This configuration covers a wide range of utility classes for spacing, sizing, typography, flexbox, grid, visibility, positioning, overflow control, text decoration, and more. Additionally, you have the option to modify or extend this list through custom component definitions, providing a flexible way to tailor TailwindCSS styling within your flect applications. 39 | -------------------------------------------------------------------------------- /docs/src/documentation/main.py: -------------------------------------------------------------------------------- 1 | from flect import flect 2 | 3 | from documentation import app as document_app 4 | from documentation.config import settings 5 | 6 | # PREBUILT_URI = str(pathlib.Path(__file__).parent.parent.parent / "docs-ui" / "dist" / "assets") 7 | PREBUILT_URI = "https://unpkg.com/docs-ui@0.1.10/dist/assets" 8 | 9 | 10 | app = flect( 11 | document_app, 12 | docs_url="/documentation", 13 | debug=settings.debug, 14 | prebuilt_uri=PREBUILT_URI, 15 | ) 16 | -------------------------------------------------------------------------------- /docs/vercel.json: -------------------------------------------------------------------------------- 1 | { 2 | "devCommand": "uvicorn src.documentation.main:app --reload --reload-dir .", 3 | "env": { 4 | "PYTHONPATH": "src" 5 | }, 6 | "builds": [ 7 | { 8 | "src": "src/documentation/main.py", 9 | "use": "@vercel/python" 10 | } 11 | ], 12 | "routes": [ 13 | { 14 | "src": "/(.*)", 15 | "dest": "src/documentation/main.py" 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /examples/todo/.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | share/python-wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .nox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | *.py,cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | cover/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | .pybuilder/ 76 | target/ 77 | 78 | # Jupyter Notebook 79 | .ipynb_checkpoints 80 | 81 | # IPython 82 | profile_default/ 83 | ipython_config.py 84 | 85 | # pyenv 86 | # For a library or package, you might want to ignore these files since the code is 87 | # intended to run in multiple environments; otherwise, check them in: 88 | # .python-version 89 | 90 | # pipenv 91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 94 | # install all needed dependencies. 95 | #Pipfile.lock 96 | 97 | # poetry 98 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 99 | # This is especially recommended for binary packages to ensure reproducibility, and is more 100 | # commonly ignored for libraries. 101 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 102 | #poetry.lock 103 | 104 | # pdm 105 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 106 | #pdm.lock 107 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 108 | # in version control. 109 | # https://pdm.fming.dev/#use-with-ide 110 | .pdm.toml 111 | 112 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 113 | __pypackages__/ 114 | 115 | # Celery stuff 116 | celerybeat-schedule 117 | celerybeat.pid 118 | 119 | # SageMath parsed files 120 | *.sage.py 121 | 122 | # Environments 123 | .env 124 | .venv 125 | env/ 126 | venv/ 127 | ENV/ 128 | env.bak/ 129 | venv.bak/ 130 | 131 | # Spyder project settings 132 | .spyderproject 133 | .spyproject 134 | 135 | # Rope project settings 136 | .ropeproject 137 | 138 | # mkdocs documentation 139 | /site 140 | 141 | # mypy 142 | .mypy_cache/ 143 | .dmypy.json 144 | dmypy.json 145 | 146 | # Pyre type checker 147 | .pyre/ 148 | 149 | # pytype static type analyzer 150 | .pytype/ 151 | 152 | # Cython debug symbols 153 | cython_debug/ 154 | 155 | # PyCharm 156 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 157 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 158 | # and can be added to the global gitignore or merged into this file. For a more nuclear 159 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 160 | #.idea/ 161 | 162 | todos.json 163 | -------------------------------------------------------------------------------- /examples/todo/.python-version: -------------------------------------------------------------------------------- 1 | 3.9 2 | -------------------------------------------------------------------------------- /examples/todo/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: dev 2 | dev: 3 | export PYTHONPATH=src && uvicorn src.todo.main:app --reload 4 | -------------------------------------------------------------------------------- /examples/todo/README.md: -------------------------------------------------------------------------------- 1 | # cookiecutter-flect 2 | 3 | Describe your project here. 4 | -------------------------------------------------------------------------------- /examples/todo/pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "todo" 3 | version = "0.1.0" 4 | description = "todo" 5 | authors = [] 6 | dependencies = [ 7 | "flect", 8 | ] 9 | readme = "README.md" 10 | requires-python = ">= 3.9" 11 | 12 | [build-system] 13 | requires = ["hatchling"] 14 | build-backend = "hatchling.build" 15 | 16 | [tool.rye] 17 | managed = true 18 | dev-dependencies = [] 19 | 20 | [tool.hatch.metadata] 21 | allow-direct-references = true 22 | 23 | [tool.hatch.build.targets.wheel] 24 | packages = ["src/todo"] 25 | -------------------------------------------------------------------------------- /examples/todo/requirements-dev.lock: -------------------------------------------------------------------------------- 1 | # generated by rye 2 | # use `rye lock` or `rye sync` to update this lockfile 3 | # 4 | # last locked with the following flags: 5 | # pre: false 6 | # features: [] 7 | # all-features: false 8 | # with-sources: false 9 | 10 | -e file:. 11 | annotated-types==0.6.0 12 | # via pydantic 13 | anyio==4.3.0 14 | # via starlette 15 | click==8.1.7 16 | # via uvicorn 17 | exceptiongroup==1.2.0 18 | # via anyio 19 | fastapi==0.110.0 20 | # via flect 21 | flect==0.1.1 22 | # via todo 23 | h11==0.14.0 24 | # via uvicorn 25 | idna==3.6 26 | # via anyio 27 | pydantic==2.6.3 28 | # via fastapi 29 | # via flect 30 | pydantic-core==2.16.3 31 | # via pydantic 32 | pyromark==0.3.0 33 | # via flect 34 | sniffio==1.3.1 35 | # via anyio 36 | starlette==0.36.3 37 | # via fastapi 38 | typing-extensions==4.10.0 39 | # via anyio 40 | # via fastapi 41 | # via pydantic 42 | # via pydantic-core 43 | # via starlette 44 | # via uvicorn 45 | uvicorn==0.27.1 46 | # via flect 47 | -------------------------------------------------------------------------------- /examples/todo/requirements.lock: -------------------------------------------------------------------------------- 1 | # generated by rye 2 | # use `rye lock` or `rye sync` to update this lockfile 3 | # 4 | # last locked with the following flags: 5 | # pre: false 6 | # features: [] 7 | # all-features: false 8 | # with-sources: false 9 | 10 | -e file:. 11 | annotated-types==0.6.0 12 | # via pydantic 13 | anyio==4.3.0 14 | # via starlette 15 | click==8.1.7 16 | # via uvicorn 17 | exceptiongroup==1.2.0 18 | # via anyio 19 | fastapi==0.110.0 20 | # via flect 21 | flect==0.1.1 22 | # via todo 23 | h11==0.14.0 24 | # via uvicorn 25 | idna==3.6 26 | # via anyio 27 | pydantic==2.6.3 28 | # via fastapi 29 | # via flect 30 | pydantic-core==2.16.3 31 | # via pydantic 32 | pyromark==0.3.0 33 | # via flect 34 | sniffio==1.3.1 35 | # via anyio 36 | starlette==0.36.3 37 | # via fastapi 38 | typing-extensions==4.10.0 39 | # via anyio 40 | # via fastapi 41 | # via pydantic 42 | # via pydantic-core 43 | # via starlette 44 | # via uvicorn 45 | uvicorn==0.27.1 46 | # via flect 47 | -------------------------------------------------------------------------------- /examples/todo/src/todo/__init__.py: -------------------------------------------------------------------------------- 1 | def hello() -> str: 2 | return "Hello from cookiecutter-flect!" 3 | -------------------------------------------------------------------------------- /examples/todo/src/todo/app/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Chaoyingz/flect/57970b59f98e5ce729494e8ce2986b4eecac2138/examples/todo/src/todo/app/__init__.py -------------------------------------------------------------------------------- /examples/todo/src/todo/app/layout.py: -------------------------------------------------------------------------------- 1 | from typing import Annotated 2 | 3 | from flect import PageResponse 4 | from flect import components as c 5 | 6 | 7 | async def layout(outlet: c.AnyComponent = c.Outlet()) -> PageResponse: 8 | return PageResponse( 9 | body=c.Container( 10 | tag="div", 11 | children=[ 12 | c.Container( 13 | tag="header", 14 | class_name="h-14 border-b text-sm flex items-center px-6", 15 | children=[ 16 | c.Link( 17 | href="/", 18 | children=[ 19 | c.Text( 20 | text="Todo", 21 | class_name="font-medium text-xl", 22 | ) 23 | ], 24 | ), 25 | ] 26 | ), 27 | c.Container( 28 | tag="main", 29 | class_name="p-12", 30 | children=[outlet] 31 | ), 32 | c.Container( 33 | tag="footer", 34 | class_name="h-14 border-t text-sm flex items-center px-6", 35 | children=[ 36 | c.Text( 37 | text="Made with ❤️ by flect", 38 | ) 39 | ], 40 | ) 41 | ], 42 | ) 43 | ) 44 | -------------------------------------------------------------------------------- /examples/todo/src/todo/app/page.py: -------------------------------------------------------------------------------- 1 | import uuid 2 | 3 | from pydantic import BaseModel, Field 4 | 5 | from flect import PageResponse 6 | from flect import components as c 7 | from flect import form 8 | 9 | from todo.storage import storage 10 | 11 | 12 | class TodoInCreate(BaseModel): 13 | name: str = form.Input(placeholder="Enter task name...", max_length=16) 14 | 15 | 16 | class TodoInDB(TodoInCreate): 17 | id: str = Field(default_factory=lambda: str(uuid.uuid4())) 18 | 19 | 20 | async def page() -> PageResponse: 21 | todos = [TodoInDB(**todo) for todo in storage.list()] 22 | return PageResponse( 23 | body=c.Container( 24 | tag="section", 25 | children=[ 26 | c.Form( 27 | model=TodoInCreate, 28 | submit_url="/", 29 | class_name="mb-5 border p-5", 30 | ), 31 | c.Table( 32 | datasets=todos, 33 | ) 34 | ], 35 | ), 36 | ) 37 | -------------------------------------------------------------------------------- /examples/todo/src/todo/app/route.py: -------------------------------------------------------------------------------- 1 | from flect.actions import Notify 2 | from flect.response import ActionResponse 3 | 4 | from todo.app.page import TodoInCreate, TodoInDB 5 | from todo.storage import storage 6 | 7 | 8 | async def post(form: TodoInCreate) -> ActionResponse: 9 | storage.insert(TodoInDB(**form.dict()).model_dump()) 10 | return ActionResponse( 11 | action=Notify(title="success", style="success", description=f"todo {form.name} created."), 12 | ) 13 | -------------------------------------------------------------------------------- /examples/todo/src/todo/main.py: -------------------------------------------------------------------------------- 1 | from flect import flect 2 | 3 | from todo import app as todo_app 4 | 5 | app = flect(todo_app) 6 | -------------------------------------------------------------------------------- /examples/todo/src/todo/storage.py: -------------------------------------------------------------------------------- 1 | import json 2 | from pathlib import Path 3 | 4 | 5 | class Storage: 6 | JSON_URI = Path(__file__).parent / "todos.json" 7 | 8 | def __init__(self) -> None: 9 | if not self.JSON_URI.exists(): 10 | self.JSON_URI.touch(exist_ok=True) 11 | 12 | def insert(self, item: dict) -> None: 13 | with self.JSON_URI.open("a") as f: 14 | f.write(f"{json.dumps(item)}\n") 15 | 16 | def list(self) -> list[dict]: 17 | with self.JSON_URI.open("r") as f: 18 | return [json.loads(line) for line in f] 19 | 20 | 21 | storage = Storage() 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "flect", 3 | "type": "module", 4 | "workspaces": [ 5 | "src/*" 6 | ], 7 | "scripts": { 8 | "dev": "npm run --workspace=@chaoying/npm-flect build --watch", 9 | "build": "npm run --workspaces build", 10 | "prettier": "prettier --write", 11 | "lint": "eslint src --ext .ts,.tsx --report-unused-disable-directives --max-warnings 0", 12 | "lint-fix": "npm run lint -- --fix", 13 | "format": "npm run prettier -- . && npm run lint-fix" 14 | }, 15 | "prettier": { 16 | "singleQuote": true, 17 | "semi": false, 18 | "trailingComma": "all", 19 | "tabWidth": 2, 20 | "printWidth": 119, 21 | "bracketSpacing": true 22 | }, 23 | "devDependencies": { 24 | "@types/node": "^20.11.24", 25 | "@types/react": "^18.2.61", 26 | "@types/react-dom": "^18.2.19", 27 | "@typescript-eslint/eslint-plugin": "^6.21.0", 28 | "@typescript-eslint/parser": "^6.21.0", 29 | "eslint": "^8.57.0", 30 | "eslint-config-prettier": "^9.1.0", 31 | "eslint-config-standard": "^17.1.0", 32 | "eslint-plugin-react": "^7.34.0", 33 | "eslint-plugin-react-hooks": "^4.6.0", 34 | "eslint-plugin-react-refresh": "^0.4.5", 35 | "eslint-plugin-simple-import-sort": "^10.0.0", 36 | "prettier": "^3.2.5", 37 | "typescript": "^5.3.3" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - 'src/*' 3 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "flect" 3 | version = "0.2.10" 4 | description = "Turning ideas into web app fast." 5 | authors = [ 6 | {name = "Chaoying", email = "chaunceywe@gmail.com"}, 7 | ] 8 | dependencies = [ 9 | "fastapi>=0.108.0", 10 | "uvicorn>=0.25.0", 11 | "pydantic>=2.6.1", 12 | "pyromark>=0.3.0", 13 | ] 14 | requires-python = ">=3.9" 15 | readme = "README.md" 16 | license = {text = "MIT"} 17 | classifiers = [ 18 | "Development Status :: 4 - Beta", 19 | "Topic :: Internet", 20 | "License :: OSI Approved :: MIT License", 21 | "Programming Language :: Python :: 3", 22 | "Programming Language :: Python :: 3 :: Only", 23 | "Programming Language :: Python :: 3.9", 24 | "Programming Language :: Python :: 3.10", 25 | "Programming Language :: Python :: 3.11", 26 | "Programming Language :: Python :: 3.12", 27 | "Intended Audience :: Developers", 28 | "Intended Audience :: Information Technology", 29 | "Framework :: Pydantic :: 2", 30 | "Framework :: FastAPI", 31 | ] 32 | 33 | [project.urls] 34 | Homepage = "https://github.com/Chaoyingz/flect" 35 | Documentation = "https://flect.celerforge.com/" 36 | Source = "https://github.com/Chaoyingz/flect" 37 | 38 | [build-system] 39 | requires = ["hatchling"] 40 | build-backend = "hatchling.build" 41 | 42 | [tool.rye] 43 | managed = true 44 | dev-dependencies = [ 45 | "ruff>=0.1.12", 46 | "pyright>=1.1.347", 47 | "coverage>=7.4.1", 48 | "pytest>=7.4.4", 49 | "pytest-asyncio>=0.23.4", 50 | "httpx>=0.27.0", 51 | ] 52 | 53 | [tool.hatch.build] 54 | artifacts = ["src/python-flect/src/static"] 55 | only-include = ["src/python-flect/src/static", "src/python-flect/src/flect"] 56 | 57 | [tool.hatch.build.targets.wheel] 58 | packages = ["src/python-flect/src/flect"] 59 | 60 | [tool.ruff] 61 | line-length = 120 62 | target-version = "py39" 63 | 64 | [tool.ruff.lint] 65 | extend-select = ["Q", "RUF100", "UP", "I"] 66 | 67 | [tool.pyright] 68 | include = ["src/python-flect/src/flect"] 69 | extraPaths = ["src/python-flect/src/flect"] 70 | 71 | [tool.coverage.run] 72 | omit = ["src/python-flect/tests/app/"] 73 | 74 | [tool.pytest.ini_options] 75 | asyncio_mode = "auto" 76 | addopts = "-s -p no:warnings" 77 | cache_dir = ".pytest_cache/" 78 | testpaths = [ 79 | "src/python-flect/tests", 80 | "src/python-flect/src/flect", 81 | ] 82 | -------------------------------------------------------------------------------- /requirements-dev.lock: -------------------------------------------------------------------------------- 1 | # generated by rye 2 | # use `rye lock` or `rye sync` to update this lockfile 3 | # 4 | # last locked with the following flags: 5 | # pre: false 6 | # features: [] 7 | # all-features: false 8 | # with-sources: false 9 | 10 | -e file:. 11 | annotated-types==0.6.0 12 | # via pydantic 13 | anyio==4.3.0 14 | # via httpx 15 | # via starlette 16 | certifi==2024.2.2 17 | # via httpcore 18 | # via httpx 19 | click==8.1.7 20 | # via uvicorn 21 | coverage==7.4.3 22 | exceptiongroup==1.2.0 23 | # via anyio 24 | # via pytest 25 | fastapi==0.110.0 26 | # via flect 27 | h11==0.14.0 28 | # via httpcore 29 | # via uvicorn 30 | httpcore==1.0.4 31 | # via httpx 32 | httpx==0.27.0 33 | idna==3.6 34 | # via anyio 35 | # via httpx 36 | iniconfig==2.0.0 37 | # via pytest 38 | nodeenv==1.8.0 39 | # via pyright 40 | packaging==23.2 41 | # via pytest 42 | pluggy==1.4.0 43 | # via pytest 44 | pydantic==2.6.4 45 | # via fastapi 46 | # via flect 47 | pydantic-core==2.16.3 48 | # via pydantic 49 | pyright==1.1.351 50 | pyromark==0.3.0 51 | # via flect 52 | pytest==8.0.2 53 | # via pytest-asyncio 54 | pytest-asyncio==0.23.5 55 | ruff==0.2.2 56 | setuptools==69.1.1 57 | # via nodeenv 58 | sniffio==1.3.1 59 | # via anyio 60 | # via httpx 61 | starlette==0.36.3 62 | # via fastapi 63 | tomli==2.0.1 64 | # via pytest 65 | typing-extensions==4.10.0 66 | # via anyio 67 | # via fastapi 68 | # via pydantic 69 | # via pydantic-core 70 | # via starlette 71 | # via uvicorn 72 | uvicorn==0.27.1 73 | # via flect 74 | -------------------------------------------------------------------------------- /requirements.lock: -------------------------------------------------------------------------------- 1 | # generated by rye 2 | # use `rye lock` or `rye sync` to update this lockfile 3 | # 4 | # last locked with the following flags: 5 | # pre: false 6 | # features: [] 7 | # all-features: false 8 | # with-sources: false 9 | 10 | -e file:. 11 | annotated-types==0.6.0 12 | # via pydantic 13 | anyio==4.3.0 14 | # via starlette 15 | click==8.1.7 16 | # via uvicorn 17 | exceptiongroup==1.2.0 18 | # via anyio 19 | fastapi==0.110.0 20 | # via flect 21 | h11==0.14.0 22 | # via uvicorn 23 | idna==3.6 24 | # via anyio 25 | pydantic==2.6.4 26 | # via fastapi 27 | # via flect 28 | pydantic-core==2.16.3 29 | # via pydantic 30 | pyromark==0.3.0 31 | # via flect 32 | sniffio==1.3.1 33 | # via anyio 34 | starlette==0.36.3 35 | # via fastapi 36 | typing-extensions==4.10.0 37 | # via anyio 38 | # via fastapi 39 | # via pydantic 40 | # via pydantic-core 41 | # via starlette 42 | # via uvicorn 43 | uvicorn==0.27.1 44 | # via flect 45 | -------------------------------------------------------------------------------- /src/npm-flect-prebuilt/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { browser: true, es2020: true }, 4 | extends: [ 5 | "eslint:recommended", 6 | "plugin:@typescript-eslint/recommended", 7 | "plugin:react-hooks/recommended", 8 | ], 9 | ignorePatterns: ["dist", ".eslintrc.cjs"], 10 | parser: "@typescript-eslint/parser", 11 | plugins: ["react-refresh"], 12 | rules: { 13 | "react-refresh/only-export-components": [ 14 | "warn", 15 | { allowConstantExport: true }, 16 | ], 17 | }, 18 | }; 19 | -------------------------------------------------------------------------------- /src/npm-flect-prebuilt/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /src/npm-flect-prebuilt/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["prettier-plugin-tailwindcss"] 3 | } 4 | -------------------------------------------------------------------------------- /src/npm-flect-prebuilt/README.md: -------------------------------------------------------------------------------- 1 | # React + TypeScript + Vite 2 | 3 | This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. 4 | 5 | Currently, two official plugins are available: 6 | 7 | - [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh 8 | - [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh 9 | 10 | ## Expanding the ESLint configuration 11 | 12 | If you are developing a production application, we recommend updating the configuration to enable type aware lint rules: 13 | 14 | - Configure the top-level `parserOptions` property like this: 15 | 16 | ```js 17 | export default { 18 | // other rules... 19 | parserOptions: { 20 | ecmaVersion: "latest", 21 | sourceType: "module", 22 | project: ["./tsconfig.json", "./tsconfig.node.json"], 23 | tsconfigRootDir: __dirname, 24 | }, 25 | }; 26 | ``` 27 | 28 | - Replace `plugin:@typescript-eslint/recommended` to `plugin:@typescript-eslint/recommended-type-checked` or `plugin:@typescript-eslint/strict-type-checked` 29 | - Optionally add `plugin:@typescript-eslint/stylistic-type-checked` 30 | - Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and add `plugin:react/recommended` & `plugin:react/jsx-runtime` to the `extends` list 31 | -------------------------------------------------------------------------------- /src/npm-flect-prebuilt/components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "new-york", 4 | "rsc": false, 5 | "tsx": true, 6 | "tailwind": { 7 | "config": "tailwind.config.js", 8 | "css": "app/globals.css", 9 | "baseColor": "slate", 10 | "cssVariables": true, 11 | "prefix": "" 12 | }, 13 | "aliases": { 14 | "components": "@/components", 15 | "utils": "@/lib/utils" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/npm-flect-prebuilt/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React + TS 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/npm-flect-prebuilt/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@chaoying/flect-prebuilt", 3 | "description": "flect is a Python framework for building full-stack web applications.", 4 | "version": "0.2.10", 5 | "type": "module", 6 | "main": "dist/index.html", 7 | "scripts": { 8 | "dev": "vite", 9 | "build": "tsc && vite build", 10 | "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", 11 | "preview": "vite preview" 12 | }, 13 | "dependencies": { 14 | "class-variance-authority": "^0.7.0", 15 | "clsx": "^2.1.0", 16 | "react": "^18.2.0", 17 | "react-dom": "^18.2.0", 18 | "react-syntax-highlighter": "^15.5.0", 19 | "tailwind-merge": "^2.2.1", 20 | "tailwindcss-animate": "^1.0.7", 21 | "@chaoying/flect": "workspace:*" 22 | }, 23 | "devDependencies": { 24 | "@types/react": "^18.2.64", 25 | "@types/react-dom": "^18.2.21", 26 | "@types/react-syntax-highlighter": "^15.5.11", 27 | "@typescript-eslint/eslint-plugin": "^7.1.1", 28 | "@typescript-eslint/parser": "^7.1.1", 29 | "@vitejs/plugin-react": "^4.2.1", 30 | "autoprefixer": "^10.4.18", 31 | "eslint": "^8.57.0", 32 | "eslint-plugin-react-hooks": "^4.6.0", 33 | "eslint-plugin-react-refresh": "^0.4.5", 34 | "postcss": "^8.4.35", 35 | "prettier": "^3.2.5", 36 | "prettier-plugin-tailwindcss": "^0.5.12", 37 | "rollup-plugin-base-url": "^0.0.2", 38 | "tailwindcss": "^3.4.1", 39 | "typescript": "^5.2.2", 40 | "vite": "^5.1.6" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/npm-flect-prebuilt/postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /src/npm-flect-prebuilt/public/vite.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/npm-flect-prebuilt/src/App.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | Flect, 3 | ActionResolverProvider, 4 | ComponentResolverProvider, 5 | getMetaContent, 6 | } from "@chaoying/flect"; 7 | import { 8 | FlectActionResolver, 9 | FlectComponentResolver, 10 | } from "@chaoying/flect/components"; 11 | 12 | function App() { 13 | return ( 14 | 15 | 16 | 22 | 23 | 24 | ); 25 | } 26 | 27 | export default App; 28 | -------------------------------------------------------------------------------- /src/npm-flect-prebuilt/src/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /src/npm-flect-prebuilt/src/lib/utils.ts: -------------------------------------------------------------------------------- 1 | import { type ClassValue, clsx } from "clsx"; 2 | import { twMerge } from "tailwind-merge"; 3 | 4 | export function cn(...inputs: ClassValue[]) { 5 | return twMerge(clsx(inputs)); 6 | } 7 | -------------------------------------------------------------------------------- /src/npm-flect-prebuilt/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom/client"; 3 | import App from "./App.tsx"; 4 | import "@/globals.css"; 5 | 6 | ReactDOM.createRoot(document.getElementById("root")!).render( 7 | 8 | 9 | , 10 | ); 11 | -------------------------------------------------------------------------------- /src/npm-flect-prebuilt/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /src/npm-flect-prebuilt/tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | darkMode: ["class"], 4 | content: [ 5 | "./pages/**/*.{ts,tsx}", 6 | "./components/**/*.{ts,tsx}", 7 | "./app/**/*.{ts,tsx}", 8 | "./src/**/*.{ts,tsx}", 9 | ], 10 | // prefix: 'fp', 11 | theme: { 12 | container: { 13 | center: true, 14 | padding: "2rem", 15 | screens: { 16 | "2xl": "1400px", 17 | }, 18 | }, 19 | extend: { 20 | colors: { 21 | border: "hsl(var(--border))", 22 | input: "hsl(var(--input))", 23 | ring: "hsl(var(--ring))", 24 | background: "hsl(var(--background))", 25 | foreground: "hsl(var(--foreground))", 26 | primary: { 27 | DEFAULT: "hsl(var(--primary))", 28 | foreground: "hsl(var(--primary-foreground))", 29 | }, 30 | secondary: { 31 | DEFAULT: "hsl(var(--secondary))", 32 | foreground: "hsl(var(--secondary-foreground))", 33 | }, 34 | destructive: { 35 | DEFAULT: "hsl(var(--destructive))", 36 | foreground: "hsl(var(--destructive-foreground))", 37 | }, 38 | muted: { 39 | DEFAULT: "hsl(var(--muted))", 40 | foreground: "hsl(var(--muted-foreground))", 41 | }, 42 | accent: { 43 | DEFAULT: "hsl(var(--accent))", 44 | foreground: "hsl(var(--accent-foreground))", 45 | }, 46 | popover: { 47 | DEFAULT: "hsl(var(--popover))", 48 | foreground: "hsl(var(--popover-foreground))", 49 | }, 50 | card: { 51 | DEFAULT: "hsl(var(--card))", 52 | foreground: "hsl(var(--card-foreground))", 53 | }, 54 | }, 55 | borderRadius: { 56 | lg: "var(--radius)", 57 | md: "calc(var(--radius) - 2px)", 58 | sm: "calc(var(--radius) - 4px)", 59 | }, 60 | keyframes: { 61 | "accordion-down": { 62 | from: { height: "0" }, 63 | to: { height: "var(--radix-accordion-content-height)" }, 64 | }, 65 | "accordion-up": { 66 | from: { height: "var(--radix-accordion-content-height)" }, 67 | to: { height: "0" }, 68 | }, 69 | }, 70 | animation: { 71 | "accordion-down": "accordion-down 0.2s ease-out", 72 | "accordion-up": "accordion-up 0.2s ease-out", 73 | }, 74 | }, 75 | }, 76 | safelist: [ 77 | { 78 | pattern: /^(w|max-w|h|max-h)-/, 79 | }, 80 | { 81 | pattern: /^(flex|grid)-/, 82 | }, 83 | { 84 | pattern: /^(m|p)\w?-\d/, 85 | }, 86 | { 87 | pattern: /^gap-\d/, 88 | }, 89 | { 90 | pattern: /^text-(\w{2}|\d{1}\w{2})$/, 91 | }, 92 | { 93 | pattern: /^(font|justify)-/, 94 | }, 95 | "border-b", 96 | "text-center", 97 | "invisible", 98 | "absolute", 99 | "overflow-hidden", 100 | "underline", 101 | "min-h-screen", 102 | "text-primary", 103 | "text-primary-foreground", 104 | "bg-primary", 105 | "bg-primary-foreground", 106 | ], 107 | corePlugins: { 108 | preflight: false, 109 | }, 110 | plugins: [require("tailwindcss-animate")], 111 | }; 112 | -------------------------------------------------------------------------------- /src/npm-flect-prebuilt/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "baseUrl": ".", 5 | "paths": { 6 | "@/*": ["./src/*"] 7 | } 8 | }, 9 | "include": ["src"], 10 | "references": [{ "path": "./tsconfig.node.json" }] 11 | } 12 | -------------------------------------------------------------------------------- /src/npm-flect-prebuilt/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "skipLibCheck": true, 5 | "module": "ESNext", 6 | "moduleResolution": "bundler", 7 | "allowSyntheticDefaultImports": true, 8 | "strict": true 9 | }, 10 | "include": ["vite.config.ts"] 11 | } 12 | -------------------------------------------------------------------------------- /src/npm-flect-prebuilt/vite.config.ts: -------------------------------------------------------------------------------- 1 | import path from "path"; 2 | import { defineConfig } from "vite"; 3 | import react from "@vitejs/plugin-react"; 4 | import { splitVendorChunkPlugin } from "vite"; 5 | 6 | const serverConfig = { 7 | host: true, 8 | port: 3000, 9 | proxy: { 10 | "/flect": "http://localhost:8000/", 11 | }, 12 | watch: { 13 | ignored: ["!**/node_modules/@chaoying/flect/**"], 14 | }, 15 | }; 16 | 17 | export default defineConfig({ 18 | plugins: [react(), splitVendorChunkPlugin()], 19 | resolve: { 20 | alias: { 21 | "@": path.resolve(__dirname, "./src"), 22 | }, 23 | }, 24 | server: serverConfig, 25 | optimizeDeps: { 26 | exclude: ["@chaoying/flect"], 27 | }, 28 | preview: serverConfig, 29 | build: { 30 | rollupOptions: { 31 | output: { 32 | entryFileNames: `assets/[name].js`, 33 | chunkFileNames: `assets/[name].js`, 34 | assetFileNames: `assets/[name].[ext]`, 35 | }, 36 | plugins: [], 37 | }, 38 | }, 39 | }); 40 | -------------------------------------------------------------------------------- /src/npm-flect/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { browser: true, es2020: true }, 4 | extends: [ 5 | "eslint:recommended", 6 | "plugin:@typescript-eslint/recommended", 7 | "plugin:react-hooks/recommended", 8 | ], 9 | ignorePatterns: ["dist", ".eslintrc.cjs"], 10 | parser: "@typescript-eslint/parser", 11 | plugins: ["react-refresh"], 12 | rules: { 13 | "react-refresh/only-export-components": [ 14 | "warn", 15 | { allowConstantExport: true }, 16 | ], 17 | }, 18 | }; 19 | -------------------------------------------------------------------------------- /src/npm-flect/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /src/npm-flect/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["prettier-plugin-tailwindcss"] 3 | } 4 | -------------------------------------------------------------------------------- /src/npm-flect/README.md: -------------------------------------------------------------------------------- 1 | # React + TypeScript + Vite 2 | 3 | This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. 4 | 5 | Currently, two official plugins are available: 6 | 7 | - [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh 8 | - [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh 9 | 10 | ## Expanding the ESLint configuration 11 | 12 | If you are developing a production application, we recommend updating the configuration to enable type aware lint rules: 13 | 14 | - Configure the top-level `parserOptions` property like this: 15 | 16 | ```js 17 | export default { 18 | // other rules... 19 | parserOptions: { 20 | ecmaVersion: "latest", 21 | sourceType: "module", 22 | project: ["./tsconfig.json", "./tsconfig.node.json"], 23 | tsconfigRootDir: __dirname, 24 | }, 25 | }; 26 | ``` 27 | 28 | - Replace `plugin:@typescript-eslint/recommended` to `plugin:@typescript-eslint/recommended-type-checked` or `plugin:@typescript-eslint/strict-type-checked` 29 | - Optionally add `plugin:@typescript-eslint/stylistic-type-checked` 30 | - Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and add `plugin:react/recommended` & `plugin:react/jsx-runtime` to the `extends` list 31 | -------------------------------------------------------------------------------- /src/npm-flect/components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "default", 4 | "rsc": true, 5 | "tsx": true, 6 | "tailwind": { 7 | "config": "tailwind.config.js", 8 | "css": "src/globals.css", 9 | "baseColor": "slate", 10 | "cssVariables": true, 11 | "prefix": "" 12 | }, 13 | "aliases": { 14 | "components": "@/components", 15 | "utils": "@/lib/utils" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/npm-flect/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@chaoying/flect", 3 | "description": "flect is a Python framework for building full-stack web applications.", 4 | "author": "chaoying", 5 | "license": "MIT", 6 | "private": false, 7 | "version": "0.2.10", 8 | "type": "module", 9 | "main": "dist/index.js", 10 | "types": "dist/index.d.ts", 11 | "exports": { 12 | ".": { 13 | "import": "./dist/index.js", 14 | "require": "./dist/index.umd.cjs", 15 | "types": "./dist/index.d.ts" 16 | }, 17 | "./components": { 18 | "import": "./dist/components.js", 19 | "types": "./dist/components/index.d.ts" 20 | } 21 | }, 22 | "scripts": { 23 | "dev": "vite", 24 | "build": "tsc && vite build --emptyOutDir", 25 | "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", 26 | "preview": "vite preview" 27 | }, 28 | "dependencies": { 29 | "@fengkx/react-syntax-highlighter": "15.6.1", 30 | "@hookform/resolvers": "^3.3.4", 31 | "@radix-ui/react-avatar": "^1.0.4", 32 | "@radix-ui/react-checkbox": "^1.0.4", 33 | "@radix-ui/react-dialog": "^1.0.5", 34 | "@radix-ui/react-label": "^2.0.2", 35 | "@radix-ui/react-popover": "^1.0.7", 36 | "@radix-ui/react-select": "^2.0.0", 37 | "@radix-ui/react-slot": "^1.0.2", 38 | "@radix-ui/react-tooltip": "^1.0.7", 39 | "ajv": "^8.12.0", 40 | "ajv-errors": "^3.0.0", 41 | "class-variance-authority": "^0.7.0", 42 | "clsx": "^2.1.0", 43 | "cmdk": "0.2.1", 44 | "lucide-react": "^0.302.0", 45 | "react": "^18.2.0", 46 | "react-dom": "^18.2.0", 47 | "react-hook-form": "^7.51.1", 48 | "react-markdown": "^9.0.1", 49 | "react-router-dom": "^6.22.3", 50 | "remark-gfm": "^4.0.0", 51 | "sonner": "^1.4.41", 52 | "tailwind-merge": "^2.2.2", 53 | "tailwindcss-animate": "^1.0.7" 54 | }, 55 | "devDependencies": { 56 | "@tailwindcss/typography": "^0.5.10", 57 | "@thoughtbot/tailwindcss-aria-attributes": "^0.2.0", 58 | "@types/json-schema": "^7.0.15", 59 | "@types/node": "^20.11.30", 60 | "@types/react": "^18.2.67", 61 | "@types/react-dom": "^18.2.22", 62 | "@typescript-eslint/eslint-plugin": "^6.21.0", 63 | "@typescript-eslint/parser": "^6.21.0", 64 | "@vitejs/plugin-react": "^4.2.1", 65 | "autoprefixer": "^10.4.19", 66 | "eslint": "^8.57.0", 67 | "eslint-plugin-react-hooks": "^4.6.0", 68 | "eslint-plugin-react-refresh": "^0.4.6", 69 | "postcss": "^8.4.38", 70 | "rollup-plugin-base-url": "^0.0.2", 71 | "tailwindcss": "^3.4.1", 72 | "typescript": "^5.4.3", 73 | "vite": "^5.2.2", 74 | "vite-bundle-analyzer": "^0.8.3", 75 | "vite-plugin-dts": "^3.7.3", 76 | "vite-plugin-lib-inject-css": "^2.0.0" 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/npm-flect/postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /src/npm-flect/public/vite.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/npm-flect/src/application.tsx: -------------------------------------------------------------------------------- 1 | import { Router } from "@/components/routing"; 2 | import { Toaster } from "@/components/ui/sonner"; 3 | import "@/globals.css"; 4 | import { ConfigContextState, ConfigProvider } from "@/contexts/config"; 5 | import { TooltipProvider } from "@/components/ui/tooltip"; 6 | 7 | export function Flect(props: ConfigContextState) { 8 | return ( 9 | 10 | 11 | 12 | 13 | 14 | 15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /src/npm-flect/src/components/action-resolver.tsx: -------------------------------------------------------------------------------- 1 | import { ActionResolver } from "@/contexts/action-resolver"; 2 | import { 3 | dispatchEventAction, 4 | notifyAction, 5 | redirectAction, 6 | } from "@/lib/actions"; 7 | import { AnyActionProps } from "@/types"; 8 | 9 | export const FlectActionResolver: ActionResolver = (props: AnyActionProps) => { 10 | switch (props.type) { 11 | case "notify": { 12 | return () => notifyAction(props); 13 | } 14 | case "redirect": { 15 | return () => redirectAction(props); 16 | } 17 | case "dispatch-event": { 18 | return () => dispatchEventAction(props); 19 | } 20 | } 21 | 22 | return null; 23 | }; 24 | FlectActionResolver.package = "flect"; 25 | -------------------------------------------------------------------------------- /src/npm-flect/src/components/component-resolver.tsx: -------------------------------------------------------------------------------- 1 | import { AnyComponentProps } from "@/types"; 2 | 3 | import { Avatar } from "@/components/flect/avatar"; 4 | import { Button } from "@/components/flect/button"; 5 | import { Container } from "@/components/flect/container"; 6 | import { Heading } from "@/components/flect/heading"; 7 | import { Link } from "@/components/flect/link"; 8 | import { Text } from "@/components/flect/text"; 9 | import { Table } from "@/components/flect/table"; 10 | import { Outlet } from "@/components/flect/outlet"; 11 | import { Form } from "@/components/flect/form"; 12 | import { NavLink } from "@/components/flect/nav-link"; 13 | import { Paragraph } from "@/components/flect/paragraph"; 14 | import { Markdown } from "@/components/flect/markdown"; 15 | import { CopyButton } from "@/components/flect/copy-button"; 16 | import { CodeBlock } from "@/components/flect/code-block"; 17 | import { Custom } from "@/components/flect/custom"; 18 | import { ComponentResolver } from "@/contexts/component-resolver"; 19 | import { Dialog } from "@/components/flect/dialog"; 20 | import { Display } from "@/components/flect/display"; 21 | import { DataGrid } from "@/components/flect/data-grid"; 22 | import { DeferredFetch } from "@/components/flect/deferred-fetch"; 23 | 24 | export const FlectComponentResolver: ComponentResolver = ( 25 | props: AnyComponentProps, 26 | ) => { 27 | switch (props.type) { 28 | case "avatar": 29 | return ; 30 | case "button": 31 | return