├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── example_request.md │ ├── feature_request.md │ └── test_request.md ├── pull_request_template.md └── workflows │ ├── deployment_autotests.yaml │ ├── pre-release.yml │ └── release.yml ├── .gitignore ├── .pre-commit-config.yaml ├── .vscode └── settings.json ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── MANIFEST.in ├── Makefile ├── README.md ├── assets ├── PreswaldBanner.png └── demo1.gif ├── autotest └── deployments │ ├── gcp │ └── test.sh │ ├── local │ └── test.sh │ └── structured │ └── test.sh ├── docker ├── entrypoint.sh ├── preswald.Dockerfile └── test │ └── iris_app.Dockerfile ├── docs ├── cli │ ├── export.mdx │ ├── init.mdx │ ├── run.mdx │ └── tutorial.mdx ├── configuration.mdx ├── controls │ ├── checkbox.mdx │ ├── selectbox.mdx │ ├── slider.mdx │ └── text_input.mdx ├── data │ ├── get_df.mdx │ └── query.mdx ├── deprecated │ ├── alert.mdx │ ├── button.mdx │ ├── chat.mdx │ ├── playground.mdx │ ├── progress.mdx │ ├── separator.mdx │ └── spinner.mdx ├── displays │ ├── image.mdx │ ├── matplotlib.mdx │ ├── plotly.mdx │ ├── table.mdx │ └── text.mdx ├── docs.json ├── favicon.svg ├── images │ ├── alert.png │ ├── banner.gif │ ├── banner.png │ ├── chat_source_change.gif │ ├── checkbox.png │ ├── playground.png │ ├── plotly.png │ ├── preswald-install.png │ ├── progress.png │ ├── selectbox.png │ ├── sidebar.png │ ├── slider.png │ ├── table.png │ ├── topbar.png │ └── workflow.png ├── introduction.mdx ├── layout │ ├── sidebar.mdx │ ├── sizing.mdx │ └── topbar.mdx ├── logo │ ├── dark.svg │ └── light.svg ├── quickstart.mdx ├── usage │ ├── examples.mdx │ └── troubleshooting.mdx └── workflow │ ├── retrypolicy.mdx │ ├── workflow.mdx │ ├── workflow_analyzer.mdx │ └── workflow_dag.mdx ├── examples ├── earthquakes │ ├── .gitignore │ ├── README.md │ ├── data │ │ └── earthquake_data.csv │ ├── hello.py │ ├── images │ │ ├── favicon.ico │ │ └── logo.png │ ├── preswald.toml │ └── pyproject.toml ├── fires │ ├── README.md │ ├── hello.py │ ├── images │ │ ├── favicon.ico │ │ └── logo.png │ ├── mapdataall.csv │ ├── preswald.toml │ └── pyproject.toml ├── iris │ ├── .gitignore │ ├── README.md │ ├── data │ │ └── iris.csv │ ├── hello.py │ ├── images │ │ ├── favicon.ico │ │ └── logo.png │ ├── preswald.toml │ └── pyproject.toml ├── network │ ├── .gitignore │ ├── README.md │ ├── data.csv │ ├── hello.py │ ├── images │ │ ├── favicon.ico │ │ ├── logo.png │ │ └── topology.png │ ├── preswald.toml │ └── pyproject.toml └── user_event │ ├── .gitignore │ ├── README.md │ ├── data │ ├── events_flat.json │ └── events_nested.json │ ├── hello.py │ ├── images │ ├── favicon.ico │ └── logo.png │ ├── preswald.toml │ └── pyproject.toml ├── frontend ├── .gitignore ├── .prettierrc ├── components.json ├── eslint.config.js ├── index.html ├── package-lock.json ├── package.json ├── postcss.config.js ├── public │ ├── favicon.ico │ └── logo.png ├── src │ ├── App.css │ ├── App.jsx │ ├── backend │ │ ├── service.js │ │ └── worker.js │ ├── components.css │ ├── components │ │ ├── Content.jsx │ │ ├── DynamicComponents.jsx │ │ ├── Layout.jsx │ │ ├── LoadingState.jsx │ │ ├── PreswaldBadge.jsx │ │ ├── TableOfContents.jsx │ │ ├── common │ │ │ └── Tooltip.jsx │ │ ├── pages │ │ │ └── Dashboard.jsx │ │ ├── ui │ │ │ ├── alert.tsx │ │ │ ├── badge.tsx │ │ │ ├── button.tsx │ │ │ ├── card.tsx │ │ │ ├── checkbox.tsx │ │ │ ├── collapsible.tsx │ │ │ ├── hover-card.tsx │ │ │ ├── input.tsx │ │ │ ├── label.tsx │ │ │ ├── progress.jsx │ │ │ ├── scroll-area.tsx │ │ │ ├── select.tsx │ │ │ ├── separator.tsx │ │ │ ├── sheet.tsx │ │ │ ├── sidebar.tsx │ │ │ ├── skeleton.tsx │ │ │ ├── slider.tsx │ │ │ ├── table.tsx │ │ │ └── tooltip.tsx │ │ └── widgets │ │ │ ├── AlertWidget.jsx │ │ │ ├── BigNumberWidget.jsx │ │ │ ├── ButtonWidget.jsx │ │ │ ├── ChatWidget.jsx │ │ │ ├── CheckboxWidget.jsx │ │ │ ├── DAGVisualizationWidget.jsx │ │ │ ├── DataVisualizationWidget.jsx │ │ │ ├── FastplotlibWidget.jsx │ │ │ ├── GenericWidget.jsx │ │ │ ├── ImageWidget.jsx │ │ │ ├── JSONViewerWidget.jsx │ │ │ ├── MarkdownRendererWidget.jsx │ │ │ ├── MatplotlibWidget.jsx │ │ │ ├── PlaygroundWidget.jsx │ │ │ ├── ProgressWidget.jsx │ │ │ ├── SelectboxWidget.jsx │ │ │ ├── SeparatorWidget.jsx │ │ │ ├── SidebarWidget.jsx │ │ │ ├── SliderWidget.jsx │ │ │ ├── SpinnerWidget.jsx │ │ │ ├── TableViewerWidget.jsx │ │ │ ├── TextInputWidget.jsx │ │ │ ├── TopbarWidget.jsx │ │ │ └── UnknownWidget.jsx │ ├── config │ │ └── features.js │ ├── index.css │ ├── lib │ │ └── utils.js │ ├── main.jsx │ ├── services │ │ └── openai.js │ ├── styles.css │ └── utils │ │ ├── dataProcessing.js │ │ ├── extractKeyProps.js │ │ └── websocket.js ├── tailwind.config.js ├── tsconfig.json └── vite.config.js ├── preswald ├── __init__.py ├── browser │ ├── __init__.py │ ├── boot.js │ ├── entrypoint.py │ └── virtual_service.py ├── build.py ├── cli.py ├── deploy.py ├── engine │ ├── __init__.py │ ├── base_service.py │ ├── managers │ │ ├── __init__.py │ │ ├── branding.py │ │ ├── data.py │ │ └── layout.py │ ├── render_tracking.py │ ├── runner.py │ ├── server_service.py │ ├── service.py │ ├── telemetry.py │ ├── transformers │ │ ├── __init__.py │ │ ├── frame_context.py │ │ └── reactive_runtime.py │ └── utils.py ├── interfaces │ ├── __init__.py │ ├── component_return.py │ ├── components.py │ ├── data.py │ ├── dependency_tracker.py │ ├── render │ │ ├── __init__.py │ │ └── registry.py │ ├── tracked_value.py │ └── workflow.py ├── main.py ├── static │ ├── favicon.ico │ └── logo.png ├── templates │ ├── hello.py.template │ ├── preswald.toml.template │ ├── sample.csv.template │ └── secrets.toml.template ├── tutorial │ ├── .gitignore │ ├── README.md │ ├── data │ │ └── sample.csv │ ├── hello.py │ ├── images │ │ ├── favicon.ico │ │ └── logo.png │ ├── preswald.toml │ └── pyproject.toml └── utils.py ├── pyproject.toml └── tests ├── fastapi_test.html └── pyodide_test.html /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug Report 3 | about: Create a report to help us improve 4 | title: '[BUG] ' 5 | labels: bug 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 | 1. Go to '...' 15 | 2. Click on '....' 16 | 3. Scroll down to '....' 17 | 4. See error 18 | 19 | **Expected behavior** 20 | A clear and concise description of what you expected to happen. 21 | 22 | **Screenshots** 23 | If applicable, add screenshots to help explain your problem. 24 | 25 | **Environment:** 26 | - OS: [e.g. iOS] 27 | - Browser: [e.g. chrome, safari] 28 | - Version: [e.g. 22] 29 | 30 | **Additional context** 31 | Add any other context about the problem here. -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/example_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Example Request 3 | about: Request a new example for the SDK 4 | title: '[EXAMPLE] ' 5 | labels: example, documentation 6 | assignees: '' 7 | --- 8 | 9 | **Use Case Description** 10 | Describe the scenario or use case that needs to be demonstrated. 11 | 12 | **Expected Functionality** 13 | What specific SDK features should this example showcase? 14 | 15 | **Example Outline** 16 | Rough outline of how the example should work: 17 | 1. Initial setup/configuration 18 | 2. Main functionality 19 | 3. Expected output/results 20 | 21 | **Target Audience** 22 | Who is this example primarily for? (e.g., beginners, advanced users, specific use case) 23 | 24 | **Related Documentation** 25 | - Link to relevant SDK documentation 26 | - Link to related features or examples 27 | 28 | **Additional Context** 29 | Any other information that would help in creating this example. -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature Request 3 | about: Suggest an idea for this project 4 | title: '[FEATURE] ' 5 | labels: enhancement 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. -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/test_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Test Request 3 | about: Request new tests or improvements to existing tests 4 | title: '[TEST] ' 5 | labels: testing 6 | assignees: '' 7 | --- 8 | 9 | **Area to be Tested** 10 | Describe which component, feature, or functionality needs testing. 11 | 12 | **Type of Testing Needed** 13 | - [ ] Unit Tests 14 | - [ ] Integration Tests 15 | - [ ] End-to-End Tests 16 | - [ ] Performance Tests 17 | - [ ] Other (please specify) 18 | 19 | **Current Test Coverage** 20 | Describe current test coverage (if any) for this area. 21 | 22 | **Specific Test Cases** 23 | List specific scenarios that should be tested: 24 | 1. Scenario 1 25 | 2. Scenario 2 26 | 3. Edge cases to consider 27 | 28 | **Expected Test Outcomes** 29 | What should the tests verify? What are the expected results? 30 | 31 | **Dependencies** 32 | Are there any dependencies or prerequisites needed for these tests? 33 | 34 | **Additional Context** 35 | Any other information relevant to the test request. -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Pull Request 3 | about: Create a pull request to contribute to the project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | --- 8 | 9 | **Related Issue** 10 | Fixes #(issue) 11 | 12 | **Description of Changes** 13 | A clear and concise description of the changes made in this pull request. 14 | 15 | **Type of Change** 16 | - [ ] Bug fix (non-breaking change which fixes an issue) 17 | - [ ] New feature (non-breaking change which adds functionality) 18 | - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) 19 | - [ ] Documentation update 20 | - [ ] New example 21 | - [ ] Test improvement 22 | 23 | **Testing** 24 | Please describe the tests that you ran to verify your changes. Include screenshots/videos. 25 | 26 | **Checklist** 27 | - [ ] My code follows the style guidelines of this project 28 | - [ ] I have performed a self-review of my own code 29 | - [ ] I have commented my code, particularly in hard-to-understand areas 30 | - [ ] I have made corresponding changes to the documentation 31 | - [ ] My changes generate no new warnings 32 | - [ ] I have run my code against examples and ensured no errors 33 | - [ ] Any dependent changes have been merged and published in downstream modules -------------------------------------------------------------------------------- /.github/workflows/deployment_autotests.yaml: -------------------------------------------------------------------------------- 1 | name: Deployment Autotests 2 | 3 | on: 4 | workflow_dispatch: 5 | schedule: 6 | - cron: "0 0 * * *" 7 | 8 | jobs: 9 | deployment-tests: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Checkout repository 13 | uses: actions/checkout@v3 14 | 15 | - name: Set up Python 16 | uses: actions/setup-python@v4 17 | with: 18 | python-version: "3.10" 19 | 20 | - name: Install Docker 21 | uses: docker/setup-buildx-action@v2 22 | 23 | - name: Install uv 24 | run: curl -LsSf https://astral.sh/uv/install.sh | sh 25 | 26 | - name: Add uv to PATH 27 | run: echo "$HOME/.cargo/bin" >> $GITHUB_PATH 28 | 29 | - name: Build and install SDK 30 | run: | 31 | uv venv 32 | source .venv/bin/activate 33 | uv pip install -U pytest 34 | uv pip install -e . 35 | python -m preswald.build frontend 36 | uv build 37 | pip install ./dist/*.whl 38 | 39 | - name: Run local target deployment tests 40 | run: | 41 | source .venv/bin/activate 42 | chmod +x autotest/deployments/local/test.sh 43 | ./autotest/deployments/local/test.sh 44 | 45 | - name: Check test results 46 | run: | 47 | if [ $? -eq 0 ]; then 48 | echo "Deployment tests passed successfully!" 49 | exit 0 50 | else 51 | echo "Deployment tests failed!" 52 | exit 1 53 | fi 54 | -------------------------------------------------------------------------------- /.github/workflows/pre-release.yml: -------------------------------------------------------------------------------- 1 | name: Pre-Release Preswald 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | run_tests: 7 | description: "Run tests" 8 | required: true 9 | default: true 10 | type: boolean 11 | 12 | jobs: 13 | pre-release: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - name: Checkout repository 17 | uses: actions/checkout@v4 18 | 19 | - name: Set up Python 20 | uses: actions/setup-python@v4 21 | with: 22 | python-version: "3.12" 23 | 24 | - name: Install dependencies 25 | run: | 26 | python -m pip install --upgrade pip 27 | pip install build setuptools wheel twine 28 | pip install -e . 29 | 30 | - name: Install bc for version calculations 31 | run: sudo apt-get install -y bc zip 32 | 33 | - name: Set up Google Cloud SDK 34 | uses: google-github-actions/setup-gcloud@v1 35 | with: 36 | install_components: "gsutil" 37 | 38 | - name: Set up Docker Buildx 39 | uses: docker/setup-buildx-action@v3 40 | 41 | - name: Login to DockerHub 42 | uses: docker/login-action@v2 43 | with: 44 | username: ${{ secrets.DOCKERHUB_USERNAME }} 45 | password: ${{ secrets.DOCKERHUB_TOKEN }} 46 | 47 | - name: Set up Conda 48 | uses: conda-incubator/setup-miniconda@v3 49 | with: 50 | auto-update-conda: true 51 | python-version: "3.12" 52 | 53 | - name: Run pre-release process 54 | run: | 55 | if [ "${{ inputs.run_tests }}" == "true" ]; then 56 | make pre-release 57 | else 58 | # Skip tests by running individual targets 59 | make update-version build-frontend build-wheel build-docker upload-to-gcs upload-static-to-gcs 60 | fi 61 | env: 62 | DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }} 63 | DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }} 64 | PRESWALD_DEPLOYER_DEV_SA: ${{ secrets.PRESWALD_DEPLOYER_DEV_SA }} 65 | 66 | - name: Upload wheel as artifact 67 | uses: actions/upload-artifact@v4 68 | with: 69 | name: preswald-wheel 70 | path: dist/*.whl 71 | if-no-files-found: error 72 | 73 | - name: Get version info 74 | id: get_version 75 | run: echo "VERSION=$(grep 'version' pyproject.toml | grep -v '^#' | head -1 | sed -E 's/.*version = "([^"]+)".*/\1/')" >> $GITHUB_OUTPUT 76 | 77 | - name: Summary 78 | run: | 79 | echo "## Pre-release Summary" >> $GITHUB_STEP_SUMMARY 80 | echo "- Version: ${{ steps.get_version.outputs.VERSION }}" >> $GITHUB_STEP_SUMMARY 81 | echo "- Wheel: $(ls -t dist/preswald-*.whl | head -1)" >> $GITHUB_STEP_SUMMARY 82 | echo "- Docker Image: structuredlabs/preswald:latest" >> $GITHUB_STEP_SUMMARY 83 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: local 3 | hooks: 4 | - id: ruff-format 5 | name: ruff (format) 6 | entry: ruff format 7 | language: system 8 | types: [python] 9 | exclude: ^setup\.py$ 10 | 11 | - id: ruff-lint 12 | name: ruff (lint) 13 | entry: ruff check 14 | language: system 15 | types: [python] 16 | exclude: ^(setup\.py|community_gallery/.*)$ 17 | 18 | - id: prettier 19 | name: prettier 20 | entry: bash -c 'cd frontend && npx prettier --write "src/**/*.{js,jsx,css,scss,json}"' 21 | language: system 22 | types_or: [javascript, jsx, css, scss, json] 23 | files: ^frontend/ 24 | 25 | # - id: eslint 26 | # name: eslint 27 | # entry: bash -c 'cd frontend && npx eslint' 28 | # language: system 29 | # types_or: [javascript, jsx] 30 | # files: ^frontend/ 31 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "Dockerfile.template": "plaintext" 4 | } 5 | } -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # **Changelog** 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6 | 7 | --- 8 | 9 | ## [0.1.26] - 2025-01-08 10 | 11 | ### Added 12 | 13 | - Performance 14 | - data compression using pako 15 | - implement progressive loading with LTTB (Largest-Triangle-Three-Buckets) sampling 16 | - intersection observer for lazy loading visualizations 17 | - optimized chunk sizes and sampling 18 | - loading progress indicator for large datasets 19 | - zlib compression on server side for data transfer optimization 20 | - lazy loading using intersection observer for on-demand graph renders 21 | - data sampling to reduce point density for large datasets 22 | - chunk loading to prevent browser freezing 23 | - memoization and React.memo to prevent unnecessary re-renders 24 | - debounced resize handling 25 | - feature toggles for fine-tuning optimization parameters 26 | - Deployment 27 | - `preswald deploy` now works for local and cloud deployment via cloud-run 28 | - `preswald stop` works for stopping local deployments 29 | - Misc 30 | - Moved setuptools from dev to core dependency in response to our first [github issue](https://github.com/StructuredLabs/preswald/issues/28) 31 | 32 | ## [0.1.23] - 2025-01-04 33 | 34 | ### Added 35 | 36 | - Set log levels via preswald.toml + CLI 37 | 38 | ## [0.1.22] - 2025-01-03 39 | 40 | ### Added 41 | 42 | - MVP of State Management 43 | - Atom and Workflow classes provide notebook-like DAGs 44 | - Support for selective recomputation, caching 45 | - Basic dependency visualization and analysis 46 | 47 | ## [0.1.0] - 2024-12-20 48 | 49 | ### Added 50 | 51 | - Initial release of Preswald: 52 | - Core functionality for building simple, interactive data apps. 53 | - Support for Markdown, data connections, and rendering tables. 54 | - Basic theming and layout configurations. 55 | - Full CLI support for project management. 56 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # **Code of Conduct** 2 | 3 | Preswald is an open-source project dedicated to fostering an inclusive, welcoming, and supportive environment for everyone, regardless of experience level, identity, or background. We are committed to making collaboration a positive and productive experience for all contributors. 4 | 5 | --- 6 | 7 | ## **Our Pledge** 8 | 9 | In the interest of fostering an open and welcoming community, we as contributors and maintainers pledge to: 10 | 11 | - Be respectful and considerate of differing viewpoints and experiences. 12 | - Refrain from any form of harassment, discrimination, or negative behavior. 13 | - Strive for collaboration over conflict and constructive criticism over disparagement. 14 | - Make the community a safe and friendly space for everyone. 15 | 16 | --- 17 | 18 | ## **Our Standards** 19 | 20 | Examples of behavior that contributes to a positive environment include: 21 | 22 | - Using inclusive and respectful language. 23 | - Being open to constructive feedback and willing to learn from others. 24 | - Offering help and support to those in need. 25 | - Respecting the work and contributions of others. 26 | 27 | Examples of unacceptable behavior include: 28 | 29 | - Using offensive or discriminatory language or imagery. 30 | - Harassing others publicly or privately, including stalking or unwelcome attention. 31 | - Insulting or derogatory comments, and personal or political attacks. 32 | - Disrupting discussions or community activities in a negative way. 33 | 34 | --- 35 | 36 | ## **Enforcement** 37 | 38 | Instances of unacceptable behavior may be reported to the project team via [amrutha@preswald.com]. All complaints will be reviewed and investigated promptly and fairly. The project team is committed to maintaining the confidentiality of reporters. 39 | 40 | If a contributor is found to be in violation of the Code of Conduct, they may face temporary or permanent consequences, including but not limited to warnings, removal from the project, or banning from community spaces. 41 | 42 | --- 43 | 44 | ## **Scope** 45 | 46 | This Code of Conduct applies within all project spaces, including the issue tracker, discussion forums, code contributions, pull requests, and other interactions within the Preswald community. It also applies to public spaces where an individual is representing the project. 47 | 48 | --- 49 | 50 | ## **Attribution** 51 | 52 | This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org), version 2.1, available at [https://www.contributor-covenant.org/version/2/1/code_of_conduct/](https://www.contributor-covenant.org/version/2/1/code_of_conduct/). 53 | 54 | For more details, see the [FAQ](https://www.contributor-covenant.org/faq). Translations are available at [https://www.contributor-covenant.org/translations](https://www.contributor-covenant.org/translations). 55 | 56 | --- 57 | 58 | By participating in Preswald, you agree to abide by this Code of Conduct and help us create a community where everyone feels welcome. 59 | 60 | --- 61 | 62 | ### **Contact** 63 | 64 | To report violations, please email us at [amrutha@preswald.com]. We will respond as soon as possible. -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | recursive-include preswald/static * 2 | recursive-include preswald/static/assets * 3 | include preswald/static/*.html 4 | include preswald/static/*.js 5 | include preswald/static/*.css 6 | include preswald/static/*.png 7 | include preswald/static/*.ico -------------------------------------------------------------------------------- /assets/PreswaldBanner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StructuredLabs/preswald/9a9015d90cdfd787cbbe236c89f13815310ddc9b/assets/PreswaldBanner.png -------------------------------------------------------------------------------- /assets/demo1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StructuredLabs/preswald/9a9015d90cdfd787cbbe236c89f13815310ddc9b/assets/demo1.gif -------------------------------------------------------------------------------- /docker/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | if [ -d "/app/project" ]; then 5 | echo "Using mounted project files from /app/project" 6 | cd /app/project 7 | else 8 | echo "No project files mounted. Please mount your project files at /app/project" 9 | exit 1 10 | fi 11 | 12 | if [ ! -f "preswald.toml" ]; then 13 | echo "No project files mounted. Please mount your project files at /app/project" 14 | exit 1 15 | fi 16 | 17 | exec preswald run --port "${PORT:-8501}" 18 | -------------------------------------------------------------------------------- /docker/preswald.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.12-slim-bullseye as builder 2 | 3 | WORKDIR /app 4 | 5 | RUN apt-get update && \ 6 | apt-get install -y --no-install-recommends \ 7 | build-essential \ 8 | curl \ 9 | libpq-dev \ 10 | postgresql-client \ 11 | && apt-get clean \ 12 | && rm -rf /var/lib/apt/lists/* 13 | 14 | # Define an argument to check if we should use local wheel 15 | ARG USE_LOCAL_WHEEL=false 16 | COPY wheel.whl /app/wheel.whl 17 | 18 | RUN pip install --no-cache-dir --upgrade pip 19 | 20 | # Install using the appropriate method 21 | RUN if [ "$USE_LOCAL_WHEEL" = "true" ] && [ -f /app/wheel.whl ]; then \ 22 | echo "Installing from local wheel" && \ 23 | pip install --no-cache-dir --upgrade /app/wheel.whl --force-reinstall; \ 24 | else \ 25 | echo "Installing from PyPI" && \ 26 | pip install --no-cache-dir --upgrade preswald; \ 27 | fi 28 | 29 | FROM python:3.12-slim-bullseye 30 | 31 | WORKDIR /app 32 | 33 | RUN useradd -m -u 1000 preswald 34 | 35 | COPY --from=builder /usr/local/lib/python3.12/site-packages/ /usr/local/lib/python3.12/site-packages/ 36 | COPY --from=builder /usr/local/bin/preswald /usr/local/bin/ 37 | RUN chown -R preswald:preswald /usr/local/lib/python3.12/site-packages/preswald && \ 38 | chmod -R 755 /usr/local/lib/python3.12/site-packages/preswald 39 | 40 | RUN apt-get update && \ 41 | apt-get install -y --no-install-recommends \ 42 | libpq-dev \ 43 | postgresql-client \ 44 | && apt-get clean \ 45 | && rm -rf /var/lib/apt/lists/* 46 | 47 | RUN chown -R preswald:preswald /app 48 | 49 | USER preswald 50 | 51 | COPY --chown=preswald:preswald entrypoint.sh /entrypoint.sh 52 | RUN chmod +x /entrypoint.sh 53 | 54 | ENV HOST=0.0.0.0 55 | ENV PORT=8501 56 | 57 | ENTRYPOINT ["/entrypoint.sh"] 58 | -------------------------------------------------------------------------------- /docker/test/iris_app.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM --platform=linux/amd64 structuredlabs/preswald:latest 2 | 3 | 4 | COPY ../../examples/iris /app/project/ 5 | -------------------------------------------------------------------------------- /docs/cli/export.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: preswald export 3 | icon: "code" 4 | description: Build your app into a static site 5 | --- 6 | 7 | The `export` command builds your Preswald app into a static site that can be run locally or shared with others. 8 | 9 | ## Usage 10 | 11 | ```bash 12 | preswald export 13 | ``` 14 | 15 | ## What it does 16 | 17 | The export command creates a `dist/` directory containing all the files needed to run your app as a static site. This includes: 18 | 19 | - Bundled Python code (via Pyodide) 20 | - Application data and DuckDB queries 21 | - UI components and assets 22 | - All necessary dependencies 23 | 24 | ## Features 25 | 26 | - **Offline Support**: The exported app works offline in any modern browser 27 | - **Complete Bundle**: Includes all Python code, data, and DuckDB queries 28 | - **State Preservation**: Maintains app UI, logic, and reactive state 29 | - **Easy Sharing**: Can be shared as a file folder or embedded in hosting platforms 30 | 31 | ## Output 32 | 33 | The command generates a `dist/` directory with the following structure: 34 | 35 | ``` 36 | dist/ 37 | ├── index.html 38 | ├── assets/ 39 | └── data/ 40 | ``` 41 | 42 | ## Use Cases 43 | 44 | - Deploying to static hosting platforms 45 | - Sharing your app with others 46 | - Running the app locally without a development server 47 | - Creating a standalone version of your application 48 | 49 | ## Notes 50 | 51 | - The exported app maintains all functionality of the development version 52 | - No server-side components are required to run the exported app 53 | - All data processing happens client-side using Pyodide 54 | -------------------------------------------------------------------------------- /docs/cli/init.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "preswald init" 3 | icon: "code" 4 | description: "Initialize a new project directory with boilerplate files." 5 | --- 6 | 7 | # `preswald init ` 8 | 9 | The `preswald init` command initializes a new project directory and generates boilerplate files to get started quickly. 10 | 11 | ## Arguments 12 | 13 | - **`project_name` (str):** 14 | The name of the new project. 15 | 16 | ## Example Usage 17 | 18 | ```bash 19 | preswald init my_app 20 | ``` 21 | 22 | ## Generated Files 23 | 24 | When you run the `preswald init` command, the following files are created in the project directory: 25 | 26 | - **`hello.py`:** Starter application script. 27 | - **`preswald.toml`:** Configuration file for your app. 28 | - **`secrets.toml`:** Secure file for storing sensitive data. 29 | - **`Workbook.md`:** Markdown notes describing your Preswald project. 30 | - **`data/`:** Directory for some sample data. 31 | - **`images/`:** Directory with logo, favicon, and readme gif. 32 | 33 | Start building your project with these preconfigured files to save time and effort. 34 | -------------------------------------------------------------------------------- /docs/cli/run.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "preswald run" 3 | icon: "code" 4 | description: "Launch a local development server for your data app." 5 | --- 6 | 7 | # `preswald run` 8 | 9 | The `preswald run` command launches a local development server for your application, enabling you to test and interact with your app in a browser. 10 | 11 | The command will run the script defined in the entrypoint in the `preswald.toml`. 12 | 13 | ## Options 14 | 15 | - **`port` (int):** 16 | The port on which to run the Preswald server. Default is 8501 17 | - **`log-level` (str):** 18 | The log level for the Preswald server. Options are DEBUG, INFO, WARNING, ERROR, CRITICAL. Default is INFO 19 | - **`disable-new-tab` (bool):** 20 | Flag which controls whether or not a new browser window pops up to show the rendered data app. Default is false, which means that a new window will pop up. 21 | 22 | 23 | --- 24 | 25 | ## Example Usage 26 | 27 | To launch the development server for your app: 28 | 29 | ```bash 30 | preswald run 31 | ``` 32 | 33 | or 34 | 35 | ```bash 36 | preswald run --port 8504 --log-level DEBUG --disable-new-tab 37 | ``` 38 | 39 | After running the command, your application will be available locally at: 40 | 41 | ```plaintext 42 | http://localhost:8501 43 | ``` 44 | 45 | ## Detailed Explanation 46 | 47 | ### 1. **Launching the Server** 48 | 49 | When you run the command, `preswald` starts a local development server using the specified Python script. This allows you to quickly preview and debug your app in a controlled environment. 50 | 51 | ### 2. **Supported Script Format** 52 | 53 | Any Python file containing valid application logic can be used. 54 | 55 | ### 3. **Localhost Access** 56 | 57 | The app is hosted on `localhost`, which is a local server accessible only from your machine. Use the provided URL to interact with your app through a browser. 58 | 59 | ### 4. **Stopping the Server** 60 | 61 | To stop the server, press `Ctrl+C` in the terminal where the command is running. 62 | 63 | ### 5. **Error Handling** 64 | 65 | If the script cannot be executed (e.g., due to missing dependencies or syntax errors), `preswald` will provide helpful error messages to guide you. 66 | 67 | --- 68 | 69 | Use the `preswald run` command to test, iterate, and debug your app effortlessly in a local environment. 70 | -------------------------------------------------------------------------------- /docs/cli/tutorial.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "preswald tutorial" 3 | icon: "code" 4 | description: "Run a tutorial preswald project bundled with the package." 5 | --- 6 | 7 | # `preswald tutorial` 8 | 9 | The `preswald tutorial` command starts up a local preswald server for a tutorial app. 10 | 11 | ## Example Usage 12 | 13 | ```bash 14 | preswald tutorial 15 | ``` 16 | -------------------------------------------------------------------------------- /docs/controls/checkbox.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "checkbox" 3 | icon: "square-check" 4 | description: "" 5 | --- 6 | 7 | ```python 8 | checkbox(label: str, default: bool = False, size: float = 1.0) -> bool 9 | ``` 10 | 11 | The `checkbox` function adds an interactive check box to your app. 12 | 13 | ## Parameters 14 | 15 | - **`label`** _(str)_: The label displayed next to the checkbox, indicating its purpose. 16 | - **`default`** _(bool)_: _(Optional)_ The initial/default value of the checkbox. 17 | - **`size`** _(float)_: _(Optional)_ The width of the component in a row. Defaults to `1.0` (full row). See the [Layout Guide](/layout/guide) for details. 18 | 19 | 20 | Hero Light 25 | 26 | 27 | ## Returns 28 | 29 | - `bool`: `True` if checked, `False` if not. 30 | 31 | ## Usage Example 32 | 33 | Here’s an example of how to add a checkbox to your app: 34 | 35 | ```python 36 | from preswald import checkbox, text 37 | 38 | # Create a checkbox for selecting 39 | money_shown = checkbox(label="Show me the money") 40 | if money_shown: 41 | text("The money") 42 | ``` 43 | 44 | ### Key Features 45 | 46 | 1. **Customizable Label**: Set the label to distinguish between different checkboxes. 47 | 2. **Adjustable Default**: Control the default state. 48 | 49 | ### Why Use `checkbox`? 50 | 51 | Checkboxes provide a way to create conditional logic in the data app - enabling more interactive and dynamic flows for end-users. 52 | -------------------------------------------------------------------------------- /docs/controls/selectbox.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "selectbox" 3 | icon: "object-group" 4 | description: "" 5 | --- 6 | 7 | ```python 8 | selectbox( 9 | label: str, 10 | options: List[str], 11 | default: Optional[str] = None, 12 | size: float = 1.0 13 | ) -> str 14 | ``` 15 | 16 | The `selectbox` function creates a dropdown menu in your app, allowing users to select an option from a predefined list. This is ideal for scenarios where users need to choose from multiple options. 17 | 18 | ## Parameters 19 | 20 | - **`label`** _(str)_: The label displayed above the dropdown, describing its purpose. 21 | - **`options`** _(list)_: A list of options that users can select from. 22 | - **`default`** _(str)_: _(Optional)_ The default option. Must be one of the options provided. 23 | - **`size`** _(float)_: _(Optional)_ The width of the component in a row. Defaults to `1.0` (full row). See the [Layout Guide](/layout/guide) for details. 24 | 25 | 26 | Hero Light 31 | 32 | 33 | ## Returns 34 | 35 | - `str` containing the current value. 36 | 37 | ## Usage Example 38 | 39 | Here’s how to use the `selectbox` function: 40 | 41 | ```python 42 | from preswald import selectbox 43 | 44 | # Create a dropdown menu to select a dataset 45 | choice = selectbox( 46 | label="Choose Dataset", 47 | options=["Dataset A", "Dataset B", "Dataset C"] 48 | ) 49 | 50 | # Use the selected option 51 | print(f"User selected: {choice}") 52 | ``` 53 | 54 | ### Key Features 55 | 56 | 1. **Customizable Options**: Pass a list of options to tailor the dropdown to your needs. 57 | 2. **Descriptive Labels**: Provide clear labels to guide users in making their selection. 58 | 3. **Dynamic Outputs**: Capture the selected value and use it dynamically within your app. 59 | 60 | ### Example Use Case 61 | 62 | Suppose you’re building a data visualization app: 63 | 64 | - Use a `selectbox` to let users choose a dataset to analyze or visualize. 65 | - Depending on the selection, display different charts or tables. 66 | 67 | ### Why Use `selectbox`? 68 | 69 | - **Simplicity**: A dropdown menu is intuitive and familiar for users. 70 | - **Flexibility**: It handles a wide range of options without cluttering the interface. 71 | - **Interactivity**: The selected option can drive other app functionality. 72 | 73 | Streamline user choices with the `selectbox`! 🎛️ 74 | -------------------------------------------------------------------------------- /docs/controls/slider.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "slider" 3 | icon: "sliders" 4 | description: "" 5 | --- 6 | 7 | ```python 8 | slider( 9 | label: str, 10 | min_val: float = 0.0, 11 | max_val: float = 100.0, 12 | step: float = 1.0, 13 | default: Optional[float] = None, 14 | size: float = 1.0, 15 | ) -> float: 16 | ``` 17 | 18 | The `slider` function adds an interactive slider to your app, enabling users to input numerical values within a specified range. This is especially useful for dynamic controls, such as selecting a range or adjusting parameters. 19 | 20 | ## Parameters 21 | 22 | - **`label`** _(str)_: The label displayed next to the slider, indicating its purpose. 23 | - **`min_val`** _(float)_: The minimum value the slider can take. 24 | - **`max_val`** _(float)_: The maximum value the slider can take. 25 | - **`step`** _(int)_: The step size between values on the slider. 26 | - **`default`** _(float)_: _(Optional)_ The initial/default value of the slider. 27 | - **`size`** _(float)_: _(Optional)_ The width of the component in a row. Defaults to `1.0` (full row). See the [Layout Guide](/layout/guide) for details. 28 | 29 | 30 | Hero Light 35 | 36 | 37 | ## Returns 38 | 39 | - `float` containing the current value. 40 | 41 | ## Usage Example 42 | 43 | Here’s an example of how to add a slider to your app: 44 | 45 | ```python 46 | from preswald import slider 47 | 48 | # Create a slider for selecting the number of rows to display 49 | value = slider( 50 | label="Rows to Display", 51 | min_val=1, 52 | max_val=100, 53 | step=10, 54 | default=20 55 | ) 56 | 57 | # Use the selected value 58 | print(f"Slider selected value: {value}") 59 | ``` 60 | 61 | ### Key Features 62 | 63 | 1. **Customizable Range**: Define the range of values with `min_val` and `max_val`. 64 | 2. **Adjustable Precision**: Control increments using the `step` parameter. 65 | 3. **Default Selection**: Set a sensible starting point with the `default` parameter. 66 | 4. **Real-Time Input**: Values update dynamically as the user interacts with the slider. 67 | 68 | ### Why Use `slider`? 69 | 70 | Sliders provide an intuitive way to capture numerical input, enhancing user experience by making the interface interactive and responsive. 71 | 72 | Simplify user input with the `slider`! 🛠️ 73 | -------------------------------------------------------------------------------- /docs/controls/text_input.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "text_input" 3 | icon: "input-text" 4 | description: "" 5 | --- 6 | 7 | ```python 8 | text_input(label: str, placeholder: str = "", size: float = 1.0) -> str 9 | ``` 10 | 11 | The `text_input` function adds an interactive text box to capture user input in your app. It maintains state between reruns, making it perfect for forms and interactive applications. 12 | 13 | ## Parameters 14 | 15 | - **`label`** _(str)_: The label displayed next to the text input, indicating its purpose. Also used to generate a consistent ID for state management. 16 | - **`placeholder`** _(str)_: _(Optional)_ Placeholder text shown when the input is empty. Helps guide users on what to enter. 17 | - **`size`** _(float)_: _(Optional)_ The width of the component in a row. Defaults to `1.0` (full row). See the [Layout Guide](/layout/guide) for details. 18 | 19 | ## Returns 20 | 21 | - `str`: The current value of the text input. Empty string if no input has been provided. 22 | 23 | ## Usage Example 24 | 25 | Here's an example of how to use text inputs in your app: 26 | 27 | ```python 28 | from preswald import text_input, text, alert 29 | 30 | # Basic text input 31 | name = text_input(label="Enter your name", placeholder="John Doe") 32 | 33 | # Using text input in a form 34 | email = text_input(label="Email", placeholder="user@example.com") 35 | password = text_input(label="Password", placeholder="Enter password") 36 | 37 | if name and email and password: 38 | alert(f"Welcome {name}!", level="success") 39 | ``` 40 | 41 | ### Key Features 42 | 43 | 1. **Customizable Label**: Set the label to distinguish between different `text_input`s. 44 | 2. **Adjustable Placeholder**: Control the look of the textboxes for better UI/UX. 45 | 46 | 47 | ### Why Use `text_input`? 48 | 49 | Text Inputs provide a way to create get end-user text input in the data app. -------------------------------------------------------------------------------- /docs/data/get_df.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "get_df" 3 | icon: "table-cells" 4 | description: "Retrieve data from configured sources as pandas DataFrames" 5 | --- 6 | 7 | ```python 8 | get_df(source_name: str, table_name: Optional[str] = None) -> pd.DataFrame 9 | ``` 10 | 11 | The `get_df` function retrieves data from a configured source and returns it as a pandas DataFrame. For database sources (PostgreSQL, ClickHouse), a table name must be specified. 12 | 13 | ## Parameters 14 | 15 | - `source_name` (str): Name of the data source as configured in preswald.toml OR a path to a file (supports CSV, Parquet, and JSON) 16 | - `table_name` (Optional[str]): Required for database sources, specifies which table to retrieve 17 | 18 | ## Returns 19 | 20 | - `pd.DataFrame`: Data from the specified source as a pandas DataFrame 21 | 22 | ## Usage Examples 23 | 24 | Note: `connect` must be called before `get_df` can be used. 25 | 26 | ### CSV Source 27 | 28 | For CSV sources, `table_name` is not required since the entire CSV file is treated as a single table: 29 | 30 | ```python 31 | from preswald import get_df 32 | 33 | # Read entire CSV file 34 | customers_df = get_df('eq_csv') 35 | 36 | # Or, read specific CSV file 37 | customers_df = get_df('data/sample.csv') 38 | ``` 39 | 40 | ### PostgreSQL Source 41 | 42 | For PostgreSQL sources, `table_name` is required: 43 | 44 | ```python 45 | from preswald import get_df 46 | 47 | # Read specific table from PostgreSQL 48 | earthquakes_df = get_df('eq_pg', 'earthquake_events') 49 | ``` 50 | 51 | ### ClickHouse Source 52 | 53 | Similarly for ClickHouse sources, `table_name` is required: 54 | 55 | ```python 56 | from preswald import get_df 57 | 58 | # Read specific table from ClickHouse 59 | events_df = get_df('eq_clickhouse', 'events') 60 | ``` 61 | 62 | ## Error Handling 63 | 64 | The function includes comprehensive error handling: 65 | 66 | 1. Validates source existence 67 | 2. Checks for required table_name parameter for database sources 68 | 3. Handles connection and query errors 69 | 4. Provides detailed error messages through logging 70 | 71 | ## Best Practices 72 | 73 | 1. Always check if source exists in preswald.toml before calling 74 | 2. For database sources, always provide `table_name` 75 | 3. Use error handling when calling the function 76 | 4. Consider memory limitations when retrieving large datasets 77 | 78 | Example with error handling: 79 | 80 | ```python 81 | from preswald import get_df 82 | 83 | try: 84 | df = get_df('eq_pg', 'large_table') 85 | except ValueError as e: 86 | print(f"Configuration error: {e}") 87 | except Exception as e: 88 | print(f"Error retrieving data: {e}") 89 | ``` 90 | 91 | ## Related Functions 92 | 93 | - `query()`: For custom SQL queries against data sources -------------------------------------------------------------------------------- /docs/data/query.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "query" 3 | icon: "code" 4 | description: "Execute SQL queries against configured data sources" 5 | --- 6 | 7 | ```python 8 | query(sql: str, source_name: str) -> pd.DataFrame 9 | ``` 10 | 11 | The `query` function executes SQL queries against configured data sources and returns the results as a pandas DataFrame. It supports all data source types (CSV, PostgreSQL, ClickHouse). 12 | 13 | ## Parameters 14 | 15 | - `sql` (str): SQL query to execute 16 | - `source_name` (str): Name of the data source as configured in preswald.toml OR a path to a file (supports CSV, Parquet, and JSON) 17 | 18 | ## Returns 19 | 20 | - `pd.DataFrame`: Query results as a pandas DataFrame 21 | 22 | ## Usage Examples 23 | 24 | ### CSV Source 25 | 26 | For CSV sources, you can query the data using standard SQL: 27 | 28 | ```python 29 | from preswald import query 30 | 31 | # Query CSV data 32 | sql = """ 33 | SELECT 34 | customer_id, 35 | COUNT(*) as order_count 36 | FROM eq_csv 37 | GROUP BY customer_id 38 | HAVING COUNT(*) > 5 39 | """ 40 | frequent_customers = query(sql, 'eq_csv') 41 | 42 | # Or, query specific CSV file 43 | frequent_customers = query(sql, 'data/sample.csv') 44 | ``` 45 | 46 | ### PostgreSQL Source 47 | 48 | For PostgreSQL sources, queries are executed directly against the database: 49 | 50 | ```python 51 | from preswald import query 52 | 53 | # Query PostgreSQL table 54 | sql = """ 55 | SELECT 56 | date, 57 | magnitude, 58 | location 59 | FROM earthquake_events 60 | WHERE magnitude > 5.0 61 | ORDER BY magnitude DESC 62 | LIMIT 10 63 | """ 64 | major_earthquakes = query(sql, 'eq_pg') 65 | ``` 66 | 67 | ### ClickHouse Source 68 | 69 | ClickHouse queries are also supported: 70 | 71 | ```python 72 | from preswald import query 73 | 74 | # Query ClickHouse table 75 | sql = """ 76 | SELECT 77 | toDate(timestamp) as date, 78 | count() as event_count 79 | FROM events 80 | GROUP BY date 81 | ORDER BY date DESC 82 | LIMIT 7 83 | """ 84 | daily_events = query(sql, 'eq_clickhouse') 85 | ``` 86 | 87 | ## Query Engine 88 | 89 | The query function uses DuckDB as the underlying query engine: 90 | 91 | 1. For CSV files, DuckDB reads and processes the data directly 92 | 2. For PostgreSQL, queries are executed through the postgres_scanner extension 93 | 3. For ClickHouse, queries use the clickhouse_scanner extension 94 | 95 | ## Error Handling 96 | 97 | The function includes comprehensive error handling: 98 | 99 | 1. Validates source existence 100 | 2. Validates SQL syntax 101 | 3. Handles connection and query errors 102 | 4. Provides detailed error messages through logging 103 | 104 | Example with error handling: 105 | 106 | ```python 107 | from preswald import query 108 | 109 | try: 110 | results = query("SELECT * FROM events", 'eq_clickhouse') 111 | except ValueError as e: 112 | print(f"Configuration error: {e}") 113 | except Exception as e: 114 | print(f"Query error: {e}") 115 | ``` 116 | 117 | ## Best Practices 118 | 119 | 1. Always check if source exists in preswald.toml before querying 120 | 2. Use parameterized queries when possible to prevent SQL injection 121 | 3. Consider query performance and data volume 122 | 4. Include appropriate WHERE clauses to limit result sets 123 | 5. Use error handling when executing queries 124 | 125 | ## Related Functions 126 | 127 | - `get_df()`: For retrieving entire tables/datasets 128 | -------------------------------------------------------------------------------- /docs/deprecated/alert.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "alert" 3 | icon: "circle-exclamation" 4 | description: "" 5 | --- 6 | 7 | ```python 8 | alert(message: str, level: str = "info", size: float = 1.0) -> str 9 | ``` 10 | 11 | The `alert` function adds an alert notification to your app. 12 | 13 | ## Parameters 14 | 15 | - **`message`** _(str)_: The message displayed by the alert 16 | - **`level`** _(str)_: The severity level of the alert. Valid values are: "info", "success", "warning", "error", "critical" 17 | - **`size`** _(float)_: _(Optional)_ The width of the component in a row. Defaults to `1.0` (full row). See the [Layout Guide](/layout/guide) for details. 18 | 19 | 20 | Hero Light 25 | 26 | 27 | ## Returns 28 | 29 | - `str` of the provided `message` 30 | 31 | ## Usage Example 32 | 33 | Here's an example of how to add an alert to your app: 34 | 35 | ```python 36 | from preswald import alert 37 | 38 | alert(message="This is an alert!", level="critical") 39 | ``` 40 | 41 | ### Key Features 42 | 43 | 1. **Customizable Message**: Set the specific message of the alert. 44 | 2. **Adjustable Severity**: Control the urgency by setting `level` to different severity levels. 45 | 46 | ### Why Use `alert`? 47 | 48 | Alerts provide a way to render updates/notifications to the user with more urgency and different severity levels to convey importance. 49 | -------------------------------------------------------------------------------- /docs/deprecated/button.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "button" 3 | icon: "square" 4 | description: "" 5 | --- 6 | 7 | ```python 8 | button(label: str, size: float = 1.0) 9 | ``` 10 | 11 | The `button` function adds a clickable button to your app. 12 | 13 | ## Parameters 14 | 15 | - **`label`** _(str)_: The text displayed on the button 16 | - **`size`** _(float)_: _(Optional)_ The width of the component in a row. Defaults to `1.0` (full row). See the [Layout Guide](/layout/guide) for details. 17 | 18 | ## Returns 19 | 20 | - Returns a button component object (Note: Button functionality is currently under development) 21 | 22 | ## Usage Example 23 | 24 | Here's an example of how to add a button to your app: 25 | 26 | ```python 27 | from preswald import button 28 | 29 | # Create a simple button 30 | button(label="Click Me!") 31 | ``` 32 | 33 | ### Key Features 34 | 35 | 1. **Customizable Label**: Set the text that appears on the button 36 | 2. **Flexible Layout**: Control the button's width in the layout 37 | 38 | ### Why Use `button`? 39 | 40 | Buttons are fundamental UI elements that provide clear call-to-actions for users. They are essential for creating interactive applications where users need to trigger specific actions. 41 | 42 | Note: Button click functionality is currently under development. 43 | -------------------------------------------------------------------------------- /docs/deprecated/chat.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Chat" 3 | description: "Learn how to use the chat API" 4 | icon: "comment" 5 | --- 6 | 7 | The chat component enables dynamic, conversational interactions with OpenAI. This endpoint allows you to send messages and receive AI-generated responses in a natural dialogue format. 8 | 9 | ## Parameters 10 | 11 | - **`source`** _str_: The source of the conversation. This parameter is optional. 12 | - **`table`** _Optional[str]_: The name of the table to query. Optional for PostgreSQL and ClickHouse sources. 13 | 14 | ## Returns 15 | 16 | Returns a Dictionary containing: 17 | 18 | - `type`: Always "chat" 19 | - `id`: Unique component identifier 20 | - `state`: Dictionary containing: 21 | - `messages`: List of conversation messages 22 | - `config`: Dictionary containing: 23 | - `source`: The source of the conversation (optional) 24 | - `data`: Pandas DataFrame converted to records format (optional) 25 | 26 | ## Backend Implementation Details 27 | 28 | Implemented in `services/openai.js` 29 | 30 | ### Authentication 31 | 32 | - Secure API key handling via environment variables (`VITE_OPENAI_API_KEY`) 33 | - Error handling for authentication issues 34 | 35 | ### Message Processing 36 | 37 | - Manages conversation history and context 38 | - Optimizes message format for API 39 | 40 | ### Error Handling 41 | 42 | - Graceful fallbacks for API disruptions 43 | - User-friendly error messages 44 | 45 | ### API Integration 46 | 47 | - OpenAI chat completions integration 48 | - GPT-3.5-turbo implementation 49 | - Conversation threading support 50 | 51 | ## Basic Usage 52 | 53 | Here's a basic example of using the chat component: 54 | 55 | ### CSV 56 | 57 | ```python 58 | from preswald import chat 59 | 60 | # Display the chat widget. Make sure source is defined in preswald.toml 61 | chat(source="iris_csv") 62 | ``` 63 | 64 | ### PostgreSQL Source 65 | 66 | For PostgreSQL sources, `table_name` is required: 67 | 68 | ```python 69 | from preswald import chat 70 | 71 | # Read specific table from PostgreSQL 72 | earthquakes_df = chat('eq_pg', 'earthquake_events') 73 | ``` 74 | 75 | ### ClickHouse Source 76 | 77 | Similarly for ClickHouse sources, `table_name` is required: 78 | 79 | ```python 80 | from preswald import chat 81 | 82 | # Read specific table from ClickHouse 83 | events_df = chat('eq_clickhouse', 'events') 84 | ``` 85 | 86 | ### Interactive Source 87 | 88 | The chat component can be used with the selectbox component to allow the datasource to be changed interactively: 89 | 90 | ```python 91 | from preswald import chat, selectbox 92 | import tomllib 93 | 94 | # Get all data sources 95 | with open("preswald.toml", "rb") as f: 96 | config = tomllib.load(f) 97 | 98 | source_list = [] 99 | for source_path in config["data"]: 100 | source_list.append(source_path) 101 | 102 | # Create a selectbox for choosing a column to visualize 103 | source_choice = selectbox( 104 | label="Choose a dataset as chat source", 105 | options=source_list, 106 | ) 107 | 108 | # Create an AI chat window using the selected source! 109 | chat(source_choice) 110 | ``` 111 | 112 | 113 | Hero Light 118 | 119 | 120 | ## Key Features 121 | 122 | - **Natural Conversation**: Engage in fluid, context-aware dialogues with Claude 123 | - **Data Integration**: Seamlessly incorporate your csv data sources into conversations 124 | - **State Management**: Automatic handling of conversation history and context 125 | -------------------------------------------------------------------------------- /docs/deprecated/playground.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "playground" 3 | icon: "paste" 4 | description: "Interactive SQL playground for querying and visualizing data." 5 | --- 6 | 7 | ```python 8 | playground(label: str, query: str, source: str = None, size: float = 1.0) -> pd.DataFrame 9 | ``` 10 | 11 | The `playground` function allows you to create an interactive SQL playground within your application. It provides a dynamic interface for querying connected data sources and visualizing results directly. 12 | 13 | ## Parameters 14 | 15 | - **`label`** _(str)_: The display label for the playground component. This is used for identification and UI rendering. 16 | - **`query`** _(str)_: The SQL query string to execute. This supports standard SQL syntax and can include `SELECT`, `JOIN`, `WHERE`, etc. 17 | - **`source`** _(str, optional)_: _(Optional)_ The name of the specific data source to query. If not provided, the function attempts to **auto-detect** the data source from the SQL query itself. _(See "Default Behavior for `source`" below)_ 18 | - **`size`** _(float)_: _(Optional)_ The width of the component in a row. Defaults to `1.0` (full row). 19 | 20 | 21 | Playground UI Example 26 | 27 | 28 | 29 | For example: 30 | ```python 31 | playground( 32 | label="User Data", 33 | query="SELECT * FROM users WHERE age > 30" 34 | ) 35 | ``` 36 | In this case, `source` is auto-detected as `users`. 37 | 38 | **Note:** Auto-detection works best when the query has a clear `FROM` or `JOIN` clause. 39 | 40 | ## Returns 41 | 42 | - `pd.DataFrame`: The resulting DataFrame after executing the query. You can use it for further processing or visualization. 43 | 44 | ## Usage Example 45 | 46 | Here's how to create a simple playground: 47 | 48 | ```python 49 | from preswald import playground 50 | 51 | # Create the playground with auto-detection of the data source 52 | df = playground( 53 | label="User Age Filter", 54 | query="SELECT name, age FROM users WHERE age > 25" 55 | ) 56 | ``` 57 | 58 | Or, explicitly pass the data source: 59 | 60 | ```python 61 | df = playground( 62 | label="User Age Filter", 63 | query="SELECT name, age FROM users WHERE age > 25", 64 | source="users" 65 | ) 66 | ``` 67 | 68 | ## Query Execution 69 | 70 | The playground component: 71 | 1. Auto-loads any saved query state (if user modified the query previously). 72 | 2. Runs the SQL query against the provided or detected data source. 73 | 3. Renders the result interactively in the frontend. 74 | 75 | ## Error Handling 76 | 77 | - Handles invalid queries and missing data sources gracefully 78 | - Captures and displays query errors in the UI 79 | - Logs detailed debug information for troubleshooting 80 | 81 | ## Best Practices 82 | 83 | 1. Use meaningful labels to distinguish multiple playgrounds. 84 | 2. Prefer explicit `source` when working with complex queries or joins. 85 | 3. Always review auto-detected source for correctness. 86 | 4. Use appropriate `WHERE` clauses to limit large datasets. 87 | 5. Handle exceptions gracefully to improve user experience. 88 | 89 | ## Related Functions 90 | 91 | - `query()`: Executes SQL queries directly 92 | - `get_df()`: Retrieves full datasets -------------------------------------------------------------------------------- /docs/deprecated/progress.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "progress" 3 | icon: "bars-progress" 4 | description: "" 5 | --- 6 | 7 | ```python 8 | progress(label: str, value: float = 0.0, size: float = 1.0) -> float 9 | ``` 10 | 11 | The `progress` function adds a progress/work done indicator to your app. 12 | 13 | ## Parameters 14 | 15 | - **`label`** _(str)_: The label displayed in the progress component, indicating its purpose. 16 | - **`value`** _(float)_: _(Optional)_ The current progress value (0.0 to 100.0). Values are automatically clamped to this range and rounded to the nearest integer for display. 17 | - **`size`** _(float)_: _(Optional)_ The width of the component in a row. Defaults to `1.0` (full row). See the [Layout Guide](/layout/guide) for details. 18 | 19 | 20 | Hero Light 25 | 26 | 27 | ## Returns 28 | 29 | - `float`: the current `value` 30 | 31 | ## Usage Example 32 | 33 | Here's an example of how to add a progress indicator to your app: 34 | 35 | ```python 36 | from preswald import progress 37 | import time 38 | 39 | # Simple progress example 40 | progress(label="Current Progress", value=50.3) # will display as 50% 41 | 42 | # Progress in a loop 43 | for i in range(10): 44 | # Update progress as work is done 45 | progress(label="Processing Files", value=i * 10) 46 | time.sleep(0.5) # Simulate work being done 47 | ``` -------------------------------------------------------------------------------- /docs/deprecated/separator.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "separator" 3 | icon: "divide" 4 | description: "" 5 | --- 6 | 7 | ```python 8 | separator() -> Dict 9 | ``` 10 | 11 | The `separator` function is a layout control component that forces a new row in your app's layout. It's essential for organizing content and creating clear visual breaks between different sections of your application. 12 | 13 | ## Parameters 14 | 15 | This component **takes no parameters**. 16 | 17 | ## Returns 18 | 19 | - `Dict` containing the component type and id. 20 | 21 | ## Usage Example 22 | 23 | Here's how to use the `separator` function: 24 | 25 | ```python 26 | from preswald import separator, text, button 27 | 28 | # Create components that will be on the same row 29 | text("Left content", size=0.5) 30 | text("Right content", size=0.5) 31 | 32 | # Force a new row with separator 33 | separator() 34 | 35 | # These components will be on a new row 36 | button("Button 1", size=0.3) 37 | button("Button 2", size=0.3) 38 | button("Button 3", size=0.3) 39 | ``` 40 | 41 | ### Key Features 42 | - **Clean Layout**: Adds a clear visual break to structure your app's content. 43 | - **Simple to Use**: No configuration needed—just call the function. 44 | -------------------------------------------------------------------------------- /docs/deprecated/spinner.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'Spinner' 3 | description: 'Display loading states in your Preswald application' 4 | icon: 'spinner' 5 | --- 6 | 7 | The Spinner widget provides a visual loading indicator for your Preswald application. It's useful for indicating that content is being loaded or that an operation is in progress. 8 | 9 | ## Properties 10 | 11 | | Property | Type | Required | Default | Description | 12 | |------------|-----------|----------|--------------|---------------------------------------------------| 13 | | label | string | No | 'Loading...' | Text displayed below the spinner | 14 | | variant | string | No | 'default' | Visual style variant ('default' or 'card') | 15 | | className | string | No | '' | Additional CSS classes to apply to the spinner | 16 | | showLabel | boolean | No | true | Whether to show the loading text label | 17 | 18 | ## Basic Usage 19 | 20 | ```python 21 | import preswald as pw 22 | 23 | def app(): 24 | pw.spinner(label="Loading data...") 25 | 26 | pw.run(app) 27 | ``` 28 | 29 | ## Examples 30 | 31 | ### Default Spinner 32 | 33 | ```python 34 | import preswald as pw 35 | 36 | def app(): 37 | pw.spinner() # Uses default "Loading..." text 38 | ``` 39 | 40 | ### Custom Label 41 | 42 | ```python 43 | import preswald as pw 44 | 45 | def app(): 46 | pw.spinner(label="Fetching results...") 47 | ``` 48 | 49 | ### Card Variant 50 | 51 | The card variant wraps the spinner in a card container for better visual hierarchy. 52 | 53 | ```python 54 | import preswald as pw 55 | 56 | def app(): 57 | pw.spinner( 58 | label="Processing...", 59 | variant="card" 60 | ) 61 | ``` 62 | 63 | ### Hidden Label 64 | 65 | ```python 66 | import preswald as pw 67 | 68 | def app(): 69 | pw.spinner(showLabel=False) # Shows only the spinning animation 70 | ``` 71 | 72 | ### Custom Styling 73 | 74 | ```python 75 | import preswald as pw 76 | 77 | def app(): 78 | pw.spinner( 79 | label="Please wait", 80 | className="bg-gray-100 rounded-lg" 81 | ) 82 | ``` 83 | 84 | ## Notes 85 | 86 | - The spinner uses a clean, animated border design that's visually appealing and lightweight 87 | - The card variant is useful when you want to emphasize the loading state in your layout 88 | - You can customize the appearance using the className property 89 | - The spinner is centered by default and works well in both inline and block contexts 90 | -------------------------------------------------------------------------------- /docs/displays/image.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'Image' 3 | description: 'Display images in your Preswald application' 4 | icon: 'image' 5 | --- 6 | 7 | # Image 8 | 9 | The Image widget allows you to display images in your Preswald application. It's a simple wrapper around the HTML `` element 10 | that provides consistent styling and integration with the Preswald framework. 11 | 12 | ## Properties 13 | 14 | | Property | Type | Required | Default | Description | 15 | |-----------|----------|----------|---------|--------------------------------------------------| 16 | | src | string | Yes | - | The URL or path to the image | 17 | | alt | string | No | '' | Alternative text for the image | 18 | | className | string | No | '' | Additional CSS classes to apply to the image | 19 | 20 | ## Basic Usage 21 | 22 | ```python 23 | import preswald as pw 24 | 25 | def app(): 26 | pw.image(src="https://example.com/image.jpg", alt="Example image") 27 | 28 | pw.run(app) 29 | ``` 30 | 31 | ## Examples 32 | 33 | ### Display a Local Image 34 | 35 | ```python 36 | import preswald as pw 37 | 38 | def app(): 39 | pw.image(src="./assets/local-image.png", alt="Local image") 40 | 41 | pw.run(app) 42 | ``` 43 | 44 | ### Adding Custom Styling 45 | 46 | ```python 47 | import preswald as pw 48 | 49 | def app(): 50 | pw.image( 51 | src="https://example.com/image.jpg", 52 | alt="Styled image", 53 | className="rounded-lg shadow-md" 54 | ) 55 | 56 | pw.run(app) 57 | ``` 58 | 59 | ## Notes 60 | 61 | - The image widget supports common image formats like PNG, JPG, GIF, and SVG 62 | - Make sure the image source URL is accessible from your application 63 | - For local images, ensure the path is relative to your application's root directory 64 | - The widget automatically applies responsive sizing while maintaining aspect ratio 65 | -------------------------------------------------------------------------------- /docs/displays/matplotlib.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'matplotlib' 3 | description: 'Display Matplotlib plots in your Preswald application' 4 | icon: 'chart-bar' 5 | --- 6 | 7 | The Matplotlib widget allows you to seamlessly display Matplotlib plots in your Preswald application. It renders plots as images within a card container, providing a clean and consistent presentation. 8 | 9 | ## Properties 10 | 11 | | Property | Type | Required | Default | Description | 12 | |-----------|----------|----------|---------|--------------------------------------------------| 13 | | _label | string | No | - | Title text displayed above the plot | 14 | | image | string | No | - | Base64-encoded PNG image data of the plot | 15 | | className | string | No | '' | Additional CSS classes to apply to the container | 16 | 17 | ## Basic Usage 18 | 19 | ```python 20 | import preswald as pw 21 | import matplotlib.pyplot as plt 22 | import numpy as np 23 | 24 | def app(): 25 | # Create a simple plot 26 | x = np.linspace(0, 10, 100) 27 | y = np.sin(x) 28 | 29 | plt.figure(figsize=(8, 6)) 30 | plt.plot(x, y) 31 | plt.title('Sine Wave') 32 | 33 | pw.matplotlib(_label="My Plot") 34 | 35 | pw.run(app) 36 | ``` 37 | 38 | ## Examples 39 | 40 | ### Plot with Custom Title 41 | 42 | ```python 43 | import preswald as pw 44 | import matplotlib.pyplot as plt 45 | import numpy as np 46 | 47 | def app(): 48 | x = np.arange(0, 5, 0.1) 49 | y = np.exp(-x) * np.cos(2*np.pi*x) 50 | 51 | plt.figure(figsize=(10, 6)) 52 | plt.plot(x, y) 53 | plt.grid(True) 54 | 55 | pw.matplotlib(_label="Damped Oscillation") 56 | ``` 57 | 58 | ### Multiple Plots 59 | 60 | ```python 61 | import preswald as pw 62 | import matplotlib.pyplot as plt 63 | import numpy as np 64 | 65 | def app(): 66 | # First plot 67 | plt.figure(figsize=(8, 4)) 68 | x = np.linspace(-5, 5, 100) 69 | plt.plot(x, x**2) 70 | pw.matplotlib(_label="Quadratic Function") 71 | 72 | # Second plot 73 | plt.figure(figsize=(8, 4)) 74 | plt.plot(x, np.abs(x)) 75 | pw.matplotlib(_label="Absolute Function") 76 | ``` 77 | 78 | ### Custom Styling 79 | 80 | ```python 81 | import preswald as pw 82 | import matplotlib.pyplot as plt 83 | 84 | def app(): 85 | plt.figure(figsize=(10, 6)) 86 | # ... plot creation ... 87 | 88 | pw.matplotlib( 89 | _label="Custom Styled Plot", 90 | className="shadow-lg rounded-xl p-4" 91 | ) 92 | ``` 93 | 94 | ## Notes 95 | 96 | - The widget automatically handles the conversion of Matplotlib plots to images 97 | - Plots are displayed responsively, adjusting to the container width 98 | - If no plot data is available, a "No plot available" message is shown 99 | - Use `plt.figure()` to create new figures when displaying multiple plots 100 | - The widget supports all types of Matplotlib plots: line plots, scatter plots, histograms, etc. 101 | -------------------------------------------------------------------------------- /docs/displays/plotly.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "plotly" 3 | icon: "chart-pie" 4 | description: "" 5 | --- 6 | 7 | ```python 8 | plotly(fig, size: float = 1.0) -> Dict 9 | ``` 10 | 11 | The `plotly` function allows you to embed interactive Plotly charts into your application. It's a powerful way to visualize data dynamically and intuitively. 12 | 13 | ## Parameters 14 | 15 | - **`fig`** _(Plotly figure object)_: The Plotly figure object to be rendered. This can be created using Plotly Express or the Plotly Graph Objects API. 16 | - **`size`** _(float)_: _(Optional)_ The width of the component in a row. Defaults to `1.0` (full row). See the [Layout Guide](/layout/guide) for details. 17 | 18 | 19 | Hero Light 24 | 25 | 26 | ## Returns 27 | 28 | - `Dict` containing the full plotly component metadata and data. 29 | 30 | ## Usage Example 31 | 32 | Here's a quick example demonstrating how to use the `plotly` function: 33 | 34 | ```python 35 | from preswald import plotly 36 | import plotly.express as px 37 | 38 | # Create a scatter plot using Plotly Express 39 | fig = px.scatter(x=[1, 2, 3], y=[4, 5, 6], labels={"x": "X-axis", "y": "Y-axis"}) 40 | 41 | # Embed the Plotly chart into your app 42 | plotly(fig) 43 | ``` 44 | 45 | ### Creating Plotly Charts 46 | 47 | You can create a variety of charts using Plotly Express or Plotly Graph Objects, such as: 48 | 49 | - Line charts 50 | - Bar charts 51 | - Scatter plots 52 | - Pie charts 53 | - Histograms 54 | - 3D plots 55 | 56 | Here's an example of a bar chart: 57 | 58 | ```python 59 | from preswald import plotly 60 | import plotly.express as px 61 | 62 | # Data for the chart 63 | data = {"Category": ["A", "B", "C"], "Values": [10, 20, 30]} 64 | fig = px.bar(data, x="Category", y="Values", title="Sample Bar Chart") 65 | 66 | # Embed the bar chart 67 | plotly(fig) 68 | ``` 69 | 70 | ### Why Use `plotly`? 71 | 72 | - **Interactivity**: Zoom, pan, hover, and more 73 | - **Customization**: Tailor every aspect of the chart to suit your needs 74 | - **Versatility**: Supports a wide range of visualizations 75 | 76 | Visualize your data like never before with Plotly! 🌟 77 | -------------------------------------------------------------------------------- /docs/displays/table.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "table" 3 | icon: "table" 4 | description: "Display data in an interactive table format" 5 | --- 6 | 7 | ```python 8 | table(data: pd.DataFrame, title: Optional[str] = None, limit: Optional[int] = None) -> Dict 9 | ``` 10 | 11 | The `table` function provides an easy way to display datasets in an interactive table format, making it simple to explore and understand your data. 12 | 13 | ## Parameters 14 | 15 | - **`data`** _(Pandas DataFrame)_: The dataset you want to display. Must be a Pandas DataFrame or a list of dictionaries. 16 | - **`title`** _(str, optional)_: An optional title to display above the table. 17 | - **`limit`** _(int, optional)_: Maximum number of rows to display. If not specified, shows all rows. 18 | 19 | 20 | Hero Light 25 | 26 | 27 | ## Returns 28 | 29 | - `Dict` containing the table component metadata and processed data. 30 | 31 | ## Usage Example 32 | 33 | Here's an example of how to use the `table` function: 34 | 35 | ```python 36 | from preswald import table 37 | 38 | # Example DataFrame 39 | import pandas as pd 40 | 41 | data = { 42 | "Name": ["Alice", "Bob", "Charlie"], 43 | "Age": [25, 30, 35], 44 | "City": ["New York", "Los Angeles", "Chicago"] 45 | } 46 | 47 | df = pd.DataFrame(data) 48 | 49 | # Display the dataset with a title 50 | table(df, title="Employee Data") 51 | 52 | # Display just the data 53 | table(df) 54 | ``` 55 | 56 | ## Key Features 57 | 58 | 1. **Automatic Data Processing**: Handles various data types including timestamps, numpy arrays, and nested structures 59 | 2. **Interactive Display**: Renders data in a format optimized for exploration and analysis 60 | 3. **Error Handling**: Gracefully handles edge cases and provides clear error messages 61 | 4. **Flexible Input**: Accepts both Pandas DataFrames and lists of dictionaries 62 | 63 | ## Data Type Support 64 | 65 | The table component automatically handles various data types: 66 | - Basic types (strings, numbers, booleans) 67 | - Timestamps and datetime objects 68 | - Numpy arrays and numeric types 69 | - Missing values (None, NaN) 70 | - Nested data structures (lists, arrays) 71 | 72 | ## Best Practices 73 | 74 | 1. **Large Datasets**: For large datasets, consider limiting the rows before display: 75 | ```python 76 | table(df, title="First 100 Rows", limit=100) 77 | ``` 78 | 79 | 2. **Column Selection**: Select relevant columns to improve readability: 80 | ```python 81 | table(df[['Name', 'Age', 'City']]) 82 | ``` 83 | 84 | 3. **Data Preprocessing**: Clean and format data before display: 85 | ```python 86 | # Round numeric columns 87 | df_clean = df.round(2) 88 | table(df_clean) 89 | ``` 90 | -------------------------------------------------------------------------------- /docs/displays/text.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "text" 3 | icon: "font" 4 | description: "" 5 | --- 6 | 7 | ```python 8 | text(markdown_str: str, size: float = 1.0) -> str: 9 | ``` 10 | 11 | The `text` function allows you to display formatted text or Markdown in your application. It’s perfect for creating titles, headers, paragraphs, and more using the power of Markdown syntax. It also allows you to display mathematical functions with latex syntax (via katex). 12 | 13 | ## Parameters 14 | 15 | - **`markdown_str`** _(str)_: The string containing text or Markdown to be rendered. 16 | - **`size`** _(float)_: _(Optional)_ The width of the component in a row. Defaults to `1.0` (full row). See the [Layout Guide](/layout/guide) for details. 17 | 18 | ## Returns 19 | 20 | - `str` containing the input `markdown_str`. 21 | 22 | ## Usage Example 23 | 24 | Here’s an example of how to use the `text` function: 25 | 26 | ```python 27 | from preswald import text 28 | 29 | # Display a Markdown header 30 | text("# Welcome to Preswald") 31 | 32 | # Display a paragraph 33 | text("This is a **formatted text** example using Markdown.") 34 | ``` 35 | 36 | ### Markdown Support 37 | 38 | The `text` function supports the full range of Markdown syntax, including: 39 | 40 | - **Headers**: `#` for H1, `##` for H2, and so on. 41 | - **Emphasis**: `*italic*`, `**bold**`, `~~strikethrough~~`. 42 | - **Lists**: 43 | - Unordered: `- Item 1`, `- Item 2` 44 | - Ordered: `1. Item 1`, `2. Item 2` 45 | - **Links**: `[Link text](https://example.com)` 46 | - **Code blocks**: 47 | ````markdown 48 | ```python 49 | print("Hello, world!") 50 | ``` 51 | ```` 52 | - **LaTeX**: 53 | - Display an inline equation: `text("$E=mc^2$")` 54 | - Display a block equation: `text("\n\n$$\\int_0^1 x^2 \\, dx = \\frac{1}{3}$$")` 55 | 56 | - And more! 57 | -------------------------------------------------------------------------------- /docs/favicon.svg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StructuredLabs/preswald/9a9015d90cdfd787cbbe236c89f13815310ddc9b/docs/favicon.svg -------------------------------------------------------------------------------- /docs/images/alert.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StructuredLabs/preswald/9a9015d90cdfd787cbbe236c89f13815310ddc9b/docs/images/alert.png -------------------------------------------------------------------------------- /docs/images/banner.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StructuredLabs/preswald/9a9015d90cdfd787cbbe236c89f13815310ddc9b/docs/images/banner.gif -------------------------------------------------------------------------------- /docs/images/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StructuredLabs/preswald/9a9015d90cdfd787cbbe236c89f13815310ddc9b/docs/images/banner.png -------------------------------------------------------------------------------- /docs/images/chat_source_change.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StructuredLabs/preswald/9a9015d90cdfd787cbbe236c89f13815310ddc9b/docs/images/chat_source_change.gif -------------------------------------------------------------------------------- /docs/images/checkbox.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StructuredLabs/preswald/9a9015d90cdfd787cbbe236c89f13815310ddc9b/docs/images/checkbox.png -------------------------------------------------------------------------------- /docs/images/playground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StructuredLabs/preswald/9a9015d90cdfd787cbbe236c89f13815310ddc9b/docs/images/playground.png -------------------------------------------------------------------------------- /docs/images/plotly.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StructuredLabs/preswald/9a9015d90cdfd787cbbe236c89f13815310ddc9b/docs/images/plotly.png -------------------------------------------------------------------------------- /docs/images/preswald-install.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StructuredLabs/preswald/9a9015d90cdfd787cbbe236c89f13815310ddc9b/docs/images/preswald-install.png -------------------------------------------------------------------------------- /docs/images/progress.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StructuredLabs/preswald/9a9015d90cdfd787cbbe236c89f13815310ddc9b/docs/images/progress.png -------------------------------------------------------------------------------- /docs/images/selectbox.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StructuredLabs/preswald/9a9015d90cdfd787cbbe236c89f13815310ddc9b/docs/images/selectbox.png -------------------------------------------------------------------------------- /docs/images/sidebar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StructuredLabs/preswald/9a9015d90cdfd787cbbe236c89f13815310ddc9b/docs/images/sidebar.png -------------------------------------------------------------------------------- /docs/images/slider.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StructuredLabs/preswald/9a9015d90cdfd787cbbe236c89f13815310ddc9b/docs/images/slider.png -------------------------------------------------------------------------------- /docs/images/table.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StructuredLabs/preswald/9a9015d90cdfd787cbbe236c89f13815310ddc9b/docs/images/table.png -------------------------------------------------------------------------------- /docs/images/topbar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StructuredLabs/preswald/9a9015d90cdfd787cbbe236c89f13815310ddc9b/docs/images/topbar.png -------------------------------------------------------------------------------- /docs/images/workflow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StructuredLabs/preswald/9a9015d90cdfd787cbbe236c89f13815310ddc9b/docs/images/workflow.png -------------------------------------------------------------------------------- /docs/introduction.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Introduction" 3 | icon: "lightbulb" 4 | description: "Turn Python scripts into shareable, interactive data apps." 5 | --- 6 | 7 | Hero Light 12 | 13 | Preswald is a static-site generator for building interactive data apps in Python. It packages compute, data access, and UI into self-contained data apps that run locally in the browser. Built on a WASM runtime with Pyodide and DuckDB, Preswald enables portable, file-based apps that are fast, reactive, and shareable. 14 | 15 | You can think of Preswald as a lightweight alternative to heavier web app platforms. It provides built-in UI components and reactive state tracking, so you can use it to build dashboards, reports, prototypes, workflows, and notebooks that are reactive, portable, and secure by default. 16 | 17 | Preswald now includes a fully automatic reactive runtime. This means you can write ordinary Python code, and Preswald will detect dependencies, track state, and trigger only the minimal set of updates needed when your data changes. No need to explicitly define which parts of your script are reactive, it's inferred automatically. 18 | 19 | ## **Key Features** 20 | 21 | - Code-based development – Write apps in Python, not in notebooks or JS frameworks 22 | - File-first approach – One command creates a fully-packaged `.html` app 23 | - Built for computation – Use Pyodide + DuckDB directly in-browser 24 | - Composable UI – Use prebuilt components like tables, charts, forms 25 | - Reactive engine – Automatically tracks dependencies in your code and re-executes only what's necessary, enabling fast, precise updates 26 | - Local execution – No server. Runs offline, even with large data 27 | - AI-ready – Apps are fully inspectable and modifiable by agents 28 | - High-performance GPU charts – Render real-time, interactive charts using fastplotlib, with offscreen GPU acceleration and WebSocket-based streaming to the browser 29 | 30 | ## Setting Up 31 | 32 | Get started with Preswald in just a few steps. Set up your environment and start building powerful, interactive data applications right away. 33 | 34 | 35 | 40 | Run preswald locally and get everything you need to start building. 41 | 42 | 47 | Read about how Preswald and the larger data ecosystem is evolving. 48 | 49 | 50 | Book a personalized demo to explore how Preswald can help your team. 51 | 52 | 57 | Explore the GitHub repo to dig into the source code or contribute. 58 | 59 | 60 | -------------------------------------------------------------------------------- /docs/layout/sidebar.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "sidebar" 3 | icon: "table-columns" 4 | description: "Add a customizable sidebar to your Preswald app" 5 | --- 6 | 7 | ```python 8 | sidebar(defaultopen = True) -> Dict 9 | ``` 10 | 11 | The `sidebar` function adds a sidebar to your app, optionally allowing you to customize the logo and title. 12 | 13 | ## Parameters 14 | 15 | - **`defaultopen`** _(bool, default=True)_: 16 | Whether to expand the sidebar on desktop by default. 17 | 18 | - **`logo`** _(str, optional)_: 19 | A URL pointing to the logo image shown at the top of the sidebar. 20 | Example: `"https://upload.wikimedia.org/wikipedia/commons/a/a7/React-icon.svg"` 21 | 22 | - **`name`** _(str, optional)_: 23 | A string to be shown as the title next to the logo in the sidebar. 24 | Example: `"Iris Dashboard" 25 | 26 | 27 | Hero Light 32 | 33 | 34 | ## Returns 35 | 36 | - `Dict` with sidebar component data 37 | 38 | ## Usage Example 39 | 40 | Here's an example of how to add a customizable sidebar to your app: 41 | 42 | ```python 43 | from preswald import sidebar 44 | 45 | sidebar( 46 | defaultopen=True, 47 | logo="https://upload.wikimedia.org/wikipedia/commons/a/a7/React-icon.svg", 48 | name="Iris Dashboard" 49 | ) 50 | ``` 51 | 52 | ### Key Features 53 | 54 | 1. **Custom Branding**: Set a custom logo and title to match your brand or app. 55 | 2. **Control**: You can use sidebar only if needed which gives you the full control. 56 | 3. **Easy to use**: No configuration needed—just call the function. 57 | 58 | ### Why Use `sidebar`? 59 | 60 | The sidebar helps organize navigation in your Preswald app and can now also visually reflect your app’s identity through branding. 61 | -------------------------------------------------------------------------------- /docs/layout/sizing.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Sizing" 3 | icon: "table-cells" 4 | description: "" 5 | --- 6 | 7 | # Using the `size` Parameter in Components 8 | 9 | The `size` parameter lets you control how much space a component occupies in a row, enabling you to create dynamic and responsive layouts. 10 | 11 | --- 12 | 13 | ## How the `size` Parameter Works 14 | 15 | - **Default Behavior**: All components have a default `size=1.0`, taking up the full width of the row. 16 | - **Custom Sizes**: You can specify a `size` less than `1.0` to allow multiple components to share the same row. 17 | - **Row Limit**: The combined `size` of components in a row cannot exceed `1.0`. 18 | - **Dependency**: A component only shares a row if another component is placed after it. 19 | 20 | --- 21 | 22 | ## Example: Multiple Components in One Row 23 | 24 | Here’s an example of how to use the `size` parameter to arrange components in a row: 25 | 26 | ```python 27 | from preswald import slider, button 28 | 29 | # Two sliders sharing a row 30 | slider1 = slider("Filter 1", min_val=0.0, max_val=10.0, default=5.0, size=0.5) 31 | slider2 = slider("Filter 2", min_val=0.0, max_val=10.0, default=5.0, size=0.5) 32 | 33 | # Button and slider sharing a row 34 | submit_button = button("Submit", size=0.3) 35 | threshold_slider = slider("Threshold", min_val=0.0, max_val=100.0, default=50.0, size=0.7) 36 | ``` 37 | 38 | --- 39 | 40 | - **Flexible Layouts**: Multiple components with smaller sizes can fit side by side in a single row. 41 | - **Spacing Management**: Verify the combined sizes of all components in a row add up to `1.0` or less. 42 | -------------------------------------------------------------------------------- /docs/layout/topbar.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "topbar" 3 | icon: "window-maximize" 4 | description: "" 5 | --- 6 | 7 | ```python 8 | topbar() 9 | ``` 10 | 11 | The `topbar()` function allows you to include a fixed topbar in your app. 12 | 13 | ## Parameters 14 | 15 | - None 16 | 17 | ## Returns 18 | 19 | - Returns a topbar component object 20 | 21 | ## Usage Example 22 | 23 | Here's an example of how to use the `topbar` function: 24 | 25 | ```python 26 | from preswald import topbar 27 | 28 | #add the topbar 29 | topbar() 30 | ``` 31 | 32 | This will add the topbar to your app: 33 | 34 | 35 | Hero Light 40 | -------------------------------------------------------------------------------- /docs/quickstart.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Quickstart" 3 | icon: "bolt" 4 | description: "Build interactive data apps that run locally in the browser with Preswald" 5 | --- 6 | 7 | 8 | 9 | 10 | 11 | ```bash 12 | pip install preswald 13 | 14 | or 15 | 16 | uv pip install preswald 17 | ``` 18 | 19 | Need the latest version? Upgrade anytime: 20 | 21 | ```bash 22 | pip install --upgrade preswald 23 | 24 | or 25 | 26 | uv pip install --upgrade preswald 27 | ``` 28 | 29 | 30 | 31 | 32 | 33 | Run these commands to bootstrap your first Preswald app: 34 | 35 | ```bash 36 | preswald init my_project 37 | cd my_project 38 | ``` 39 | 40 | This will create a scaffolded project with the following: 41 | 42 | - **`hello.py`**: Your main Python script where you'll write your app logic 43 | - **`preswald.toml`**: Configuration for your app's metadata, runtime settings, and branding 44 | - **`secrets.toml`**: Secure storage for sensitive data like API keys 45 | - **`data/`**: Directory for your input data files 46 | - **`images/`**: Directory for custom branding assets 47 | - **`.gitignore`**: Pre-configured to exclude sensitive files from version control 48 | 49 | 50 | 51 | 52 | 53 | Open `hello.py` and edit it with the following content: 54 | 55 | ```python 56 | from preswald import text, table, get_df 57 | 58 | # Add a title 59 | text("# Welcome to Preswald") 60 | 61 | # Load and display data 62 | df = get_df("data/sample.csv") 63 | table(df) 64 | ``` 65 | 66 | This simple example demonstrates Preswald's key features: 67 | - Python-based development with built-in UI components 68 | - Direct data access with DuckDB integration 69 | - Reactive updates powered by Pyodide in the browser 70 | 71 | #### Run It Locally 72 | 73 | Launch your app locally with this command: 74 | 75 | ```bash 76 | preswald run 77 | ``` 78 | 79 | Open your browser and navigate to **[http://localhost:8501](http://localhost:8501)**. Your app will run entirely in the browser, with no server required. 80 | 81 | 82 | 83 | 84 | 85 | When you're ready to share your app, export it as a static site: 86 | 87 | ```bash 88 | preswald export 89 | ``` 90 | 91 | This creates a `dist/` folder containing your complete app, including: 92 | - All Python code (bundled via Pyodide) 93 | - Data files and DuckDB queries 94 | - UI components and styling 95 | - Everything needed to run offline in any modern browser 96 | 97 | You can now share this folder directly or host it on any static hosting platform. 98 | 99 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /docs/usage/examples.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Examples" 3 | icon: "book" 4 | --- 5 | 6 | ## **Hello World** 7 | 8 | Display a simple "Hello, World!" message using Preswald: 9 | 10 | ```python 11 | from preswald import text 12 | 13 | text("# Hello, World!") 14 | ``` 15 | 16 | --- 17 | 18 | ## **Data Viewer** 19 | 20 | Connect to a CSV file and display the data using Preswald's viewer: 21 | 22 | ```python 23 | from preswald import connect, get_df, table 24 | 25 | data = get_df("example_data.csv") 26 | table(data) 27 | ``` 28 | 29 | Connect to a JSON file and display the data using Preswald's viewer: 30 | 31 | ```python 32 | from preswald import connect, get_df, view 33 | 34 | data = get_df("user_event.csv") 35 | view(data) 36 | ``` 37 | 38 | --- 39 | 40 | ## **Interactive Dashboard** 41 | 42 | Create an interactive dashboard where users can control how many rows of data to display: 43 | 44 | ```python 45 | from preswald import text, slider, table 46 | 47 | text("# Interactive Dashboard") 48 | 49 | rows = slider("Rows to Display", min_val=5, max_val=50, default=10) 50 | table(data, limit=rows) 51 | ``` 52 | 53 | --- 54 | 55 | ## **Automatic Reactivity** 56 | Preswald automatically detects dependencies between top level expressions and selectively recomputes only the parts of your script that need to update. 57 | 58 | ```python 59 | from preswald import slider, text 60 | 61 | base = slider("Base", min_val=1, max_val=10, default=2) 62 | double = base * 2 63 | text(f"Double: {double}") 64 | ``` 65 | 66 | You can also include side effects like logging: 67 | 68 | ```python 69 | import logging 70 | from preswald import slider 71 | 72 | logger = logging.getLogger(__name__) 73 | 74 | val = slider("Level", min_val=0, max_val=5, default=1) 75 | logger.info(f"Slider moved: {val}") 76 | ``` 77 | 78 | Preswald will: 79 | 80 | - Lift each top level statement into an atom 81 | - Automatically track dependencies like double -> base 82 | - Ensure logger.info(...) runs again only when val changes 83 | 84 | No decorators or workflow setup required, just plain Python 85 | 86 | --- 87 | 88 | ## **Tuple Unpacking + Reactive Consumers** 89 | 90 | Preswald also supports tuple unpacking and reactive consumers: 91 | 92 | ```python 93 | from preswald import slider, text 94 | 95 | def compute_pair(n): 96 | return (n, n * 2) 97 | 98 | val = slider("Input", min_val=1, max_val=10, default=3) 99 | a, b = compute_pair(val) 100 | text(f"a: {a}, b: {b}") 101 | ``` 102 | 103 | --- 104 | 105 | ## Learn More 106 | 107 | To explore these examples in depth and discover additional use cases, check out the [Preswald Examples](https://github.com/StructuredLabs/preswald/tree/main/examples). You'll find comprehensive guides and example projects to help you make the most of Preswald. 108 | -------------------------------------------------------------------------------- /docs/workflow/workflow_dag.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Workflow DAG" 3 | icon: "diagram-project" 4 | description: "" 5 | --- 6 | 7 | ```python 8 | workflow_dag(workflow: Workflow, title: str = "Workflow Dependency Graph") -> Dict 9 | ``` 10 | 11 | The `workflow_dag` function renders a Directed Acyclic Graph (DAG) visualization for a given workflow. This helps you visualize dependencies, execution status, and relationships within the workflow, making it easier to understand and debug complex processes. 12 | 13 | ## Parameters 14 | 15 | - **`workflow`** _(Workflow)_: A workflow object representing the process or pipeline to be visualized. 16 | - **`title`** _(str)_: _(Optional)_ The title displayed above the visualization. Defaults to `"Workflow Dependency Graph"`. 17 | 18 | 19 | Hero Light 24 | 25 | 26 | ## Returns 27 | 28 | - `Dict`: A component containing the DAG visualization data with node information. 29 | 30 | ## Node Information 31 | 32 | Each node in the DAG contains the following information: 33 | 34 | - **name**: The name of the task/node 35 | - **status**: Current execution status 36 | - **execution_time**: Time taken for execution 37 | - **attempts**: Number of execution attempts 38 | - **error**: Any error information if failed 39 | - **dependencies**: List of dependent tasks 40 | - **force_recompute**: Whether the task is marked for recomputation 41 | 42 | ## Usage Example 43 | 44 | Here's an example of how to visualize a workflow using `workflow_dag`: 45 | 46 | ```python 47 | from preswald import workflow_dag, Workflow 48 | 49 | # Create a workflow object 50 | workflow = Workflow() 51 | 52 | # Add some tasks to the workflow 53 | @workflow.task() 54 | def task_a(): 55 | return "A" 56 | 57 | @workflow.task(depends_on=[task_a]) 58 | def task_b(): 59 | return "B" 60 | 61 | # Render the workflow DAG 62 | workflow_dag(workflow, title="Example Workflow") 63 | ``` 64 | 65 | ### Key Features 66 | 67 | 1. **Rich Node Data**: Each node shows detailed execution information 68 | 2. **Dependency Visualization**: Clear representation of task dependencies 69 | 3. **Status Tracking**: Visual indicators for task status (success/failure/pending) 70 | 4. **Performance Insights**: Shows execution time and attempt counts 71 | 5. **Error Visibility**: Displays error information for failed tasks 72 | 73 | ### Common Use Cases 74 | 75 | Workflow DAGs are essential for: 76 | 77 | - Debugging complex workflows 78 | - Monitoring task execution 79 | - Understanding dependency chains 80 | - Identifying bottlenecks 81 | - Tracking execution progress 82 | - Visualizing pipeline structure 83 | 84 | ### Why Use `workflow_dag`? 85 | 86 | The workflow DAG visualization helps you: 87 | 88 | - Understand complex task relationships 89 | - Monitor workflow execution status 90 | - Debug failed tasks 91 | - Optimize workflow structure 92 | - Share workflow insights with team members 93 | 94 | Perfect for both development and production monitoring of your workflow pipelines! 🔍 95 | -------------------------------------------------------------------------------- /examples/earthquakes/.gitignore: -------------------------------------------------------------------------------- 1 | secrets.toml 2 | .preswald_deploy 3 | -------------------------------------------------------------------------------- /examples/earthquakes/README.md: -------------------------------------------------------------------------------- 1 | # Preswald Project 2 | 3 | ## Setup 4 | 1. Configure your data connections in `preswald.toml` 5 | 2. Add sensitive information (passwords, API keys) to `secrets.toml` 6 | 3. Run your app with `preswald run` 7 | -------------------------------------------------------------------------------- /examples/earthquakes/hello.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import plotly.express as px 3 | 4 | from preswald import get_df, plotly, slider, table, text 5 | 6 | 7 | # Title 8 | text("# Earthquake Analytics Dashboard 🌍") 9 | 10 | # Clickhouse section 11 | 12 | query_string = """ 13 | WITH yearly_stats AS 14 | ( 15 | SELECT 16 | toYear(Date) AS year, 17 | count() AS earthquake_count, 18 | round(avg(Magnitude), 2) AS avg_magnitude, 19 | round(max(Magnitude), 2) AS max_magnitude, 20 | round(avg(Depth), 2) AS avg_depth, 21 | countIf(Magnitude >= 6.) AS significant_quakes 22 | FROM earthquakes 23 | WHERE toYear(Date) >= 1970 24 | GROUP BY year 25 | ORDER BY year DESC 26 | ) 27 | SELECT 28 | current.year, 29 | current.earthquake_count, 30 | current.avg_magnitude, 31 | current.max_magnitude, 32 | current.avg_depth, 33 | current.significant_quakes, 34 | round(((current.earthquake_count - previous.earthquake_count) / previous.earthquake_count) * 100, 1) AS yoy_change_percent 35 | FROM yearly_stats AS current 36 | LEFT JOIN yearly_stats AS previous 37 | ON current.year = previous.year + 1 38 | ORDER BY current.year DESC 39 | LIMIT 10 40 | """ 41 | 42 | # c_data = query("SELECT * FROM earthquakes LIMIT 50;", "eq_clickhouse") 43 | # d_data = query(query_string, "eq_clickhouse") 44 | 45 | # table(c_data) 46 | # table(d_data) 47 | # --- 48 | 49 | # Slider for filtering magnitude 50 | min_magnitude = slider("Minimum Magnitude", min_val=0.0, max_val=10.0, default=5.0) 51 | 52 | # Read the data and filter based on magnitude 53 | data = get_df("earthquake_data") 54 | # data = get_df("earthquake_db", "earthquake_data") # NOTE: requires changing the column names based on what you have in postgres 55 | # Convert Magnitude column to numeric, handling any non-numeric values 56 | data["Magnitude"] = pd.to_numeric(data["Magnitude"], errors="coerce") 57 | filtered_data = data[data["Magnitude"] >= min_magnitude] 58 | 59 | # View the filtered data 60 | table(filtered_data) 61 | 62 | # Summary statistics 63 | text(f"### Total Earthquakes with Magnitude ≥ {min_magnitude}: {len(filtered_data)}") 64 | 65 | 66 | # Interactive map using Plotly 67 | text("## Earthquake Locations") 68 | fig_map = px.scatter_geo( 69 | filtered_data, 70 | lat="Latitude", 71 | lon="Longitude", 72 | color="Magnitude", 73 | size="Magnitude", 74 | hover_name="ID", 75 | title="Earthquake Map", 76 | ) 77 | plotly(fig_map) 78 | 79 | # Magnitude distribution 80 | fig_hist = px.histogram( 81 | filtered_data, x="Magnitude", nbins=20, title="Distribution of Magnitudes" 82 | ) 83 | plotly(fig_hist) 84 | 85 | # Depth vs. Magnitude scatter plot 86 | fig_scatter = px.scatter( 87 | filtered_data, 88 | x="Depth", 89 | y="Magnitude", 90 | color="Magnitude", 91 | title="Depth vs Magnitude", 92 | labels={"Depth": "Depth (km)", "Magnitude": "Magnitude"}, 93 | ) 94 | plotly(fig_scatter) 95 | -------------------------------------------------------------------------------- /examples/earthquakes/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StructuredLabs/preswald/9a9015d90cdfd787cbbe236c89f13815310ddc9b/examples/earthquakes/images/favicon.ico -------------------------------------------------------------------------------- /examples/earthquakes/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StructuredLabs/preswald/9a9015d90cdfd787cbbe236c89f13815310ddc9b/examples/earthquakes/images/logo.png -------------------------------------------------------------------------------- /examples/earthquakes/preswald.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | title = "Preswald Project" 3 | version = "0.1.0" 4 | port = 8501 5 | entrypoint = "hello.py" 6 | slug = "earthquakes" 7 | 8 | [branding] 9 | name = "Preswald Project" 10 | logo = "images/logo.png" 11 | favicon = "images/favicon.ico" 12 | primaryColor = "#F89613" 13 | 14 | [data.earthquake_db] 15 | # type = "postgres" 16 | host = "localhost" # PostgreSQL host 17 | port = 5432 # PostgreSQL port 18 | dbname = "earthquakes" # Database name 19 | user = "user" # Username 20 | # password is stored in secrets.toml 21 | 22 | [data.earthquake_data] 23 | type = "csv" 24 | path = "data/earthquake_data.csv" 25 | delimiter = "," 26 | header = true 27 | 28 | [data.eq_clickhouse] 29 | # type = "clickhouse" 30 | host = "localhost" 31 | port = 8123 32 | database = "default" 33 | user = "default" 34 | secure = false 35 | verify = true 36 | -------------------------------------------------------------------------------- /examples/earthquakes/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["hatchling"] 3 | build-backend = "hatchling.build" 4 | 5 | [project] 6 | name = "earthquakes" 7 | version = "0.1.0" 8 | description = "A Preswald application" 9 | requires-python = ">=3.8" 10 | dependencies = [ 11 | "preswald==0.1.33", 12 | "duckdb==1.1.3" 13 | ] 14 | 15 | [tool.black] 16 | line-length = 88 17 | target-version = ['py38'] 18 | 19 | [tool.isort] 20 | profile = "black" 21 | multi_line_output = 3 22 | 23 | [tool.hatch.build.targets.wheel] 24 | packages = ["."] -------------------------------------------------------------------------------- /examples/fires/README.md: -------------------------------------------------------------------------------- 1 | # Preswald Project 2 | 3 | ## Setup 4 | 1. Configure your data connections in `preswald.toml` 5 | 2. Add sensitive information (passwords, API keys) to `secrets.toml` 6 | 3. Run your app with `preswald run` 7 | -------------------------------------------------------------------------------- /examples/fires/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StructuredLabs/preswald/9a9015d90cdfd787cbbe236c89f13815310ddc9b/examples/fires/images/favicon.ico -------------------------------------------------------------------------------- /examples/fires/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StructuredLabs/preswald/9a9015d90cdfd787cbbe236c89f13815310ddc9b/examples/fires/images/logo.png -------------------------------------------------------------------------------- /examples/fires/preswald.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | title = "Preswald Project" 3 | version = "0.1.0" 4 | port = 8501 5 | entrypoint = "hello.py" 6 | slug = "fires" 7 | 8 | [branding] 9 | name = "Preswald Project" 10 | logo = "images/logo.png" 11 | favicon = "images/favicon.ico" 12 | primaryColor = "#F89613" 13 | 14 | [theme] 15 | primary_color = "#3498db" 16 | font = "Arial, sans-serif" 17 | 18 | [data.csv] 19 | type = "csv" 20 | path = "mapdataall.csv" 21 | 22 | [logging] 23 | level = "INFO" # Options: DEBUG, INFO, WARNING, ERROR, CRITICAL 24 | format = "%(asctime)s - %(name)s - %(levelname)s - %(message)s" 25 | -------------------------------------------------------------------------------- /examples/fires/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["hatchling"] 3 | build-backend = "hatchling.build" 4 | 5 | [project] 6 | name = "fires" 7 | version = "0.1.0" 8 | description = "A Preswald application" 9 | requires-python = ">=3.8" 10 | dependencies = [ 11 | "preswald==0.1.33", 12 | "duckdb==1.1.3" 13 | ] 14 | 15 | [tool.black] 16 | line-length = 88 17 | target-version = ['py38'] 18 | 19 | [tool.isort] 20 | profile = "black" 21 | multi_line_output = 3 22 | 23 | [tool.hatch.build.targets.wheel] 24 | packages = ["."] -------------------------------------------------------------------------------- /examples/iris/.gitignore: -------------------------------------------------------------------------------- 1 | secrets.toml 2 | .preswald_deploy 3 | .env.structured 4 | -------------------------------------------------------------------------------- /examples/iris/README.md: -------------------------------------------------------------------------------- 1 | # Preswald Project 2 | 3 | ## Setup 4 | 1. Configure your data connections in `preswald.toml` 5 | 2. Add sensitive information (passwords, API keys) to `secrets.toml` 6 | 3. Run your app with `preswald run` -------------------------------------------------------------------------------- /examples/iris/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StructuredLabs/preswald/9a9015d90cdfd787cbbe236c89f13815310ddc9b/examples/iris/images/favicon.ico -------------------------------------------------------------------------------- /examples/iris/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StructuredLabs/preswald/9a9015d90cdfd787cbbe236c89f13815310ddc9b/examples/iris/images/logo.png -------------------------------------------------------------------------------- /examples/iris/preswald.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | title = "Preswald Project" 3 | version = "0.1.0" 4 | port = 8501 5 | slug = "iris-916474" # Required: Unique identifier for your project 6 | entrypoint = "hello.py" 7 | 8 | [branding] 9 | name = "Preswald Project" 10 | logo = "images/logo.png" 11 | favicon = "images/favicon.ico" 12 | primaryColor = "#F89613" 13 | 14 | [data.iris_csv] 15 | type = "csv" 16 | path = "data/iris.csv" 17 | 18 | [logging] 19 | level = "INFO" # Options: DEBUG, INFO, WARNING, ERROR, CRITICAL 20 | format = "%(asctime)s - %(name)s - %(levelname)s - %(message)s" -------------------------------------------------------------------------------- /examples/iris/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["hatchling"] 3 | build-backend = "hatchling.build" 4 | 5 | [project] 6 | name = "preswald-app" 7 | version = "0.1.0" 8 | description = "A Preswald application" 9 | requires-python = ">=3.8" 10 | dependencies = [ 11 | "preswald" 12 | ] 13 | 14 | [tool.hatch.build.targets.wheel] 15 | packages = ["."] 16 | 17 | [tool.black] 18 | line-length = 88 19 | target-version = ['py38'] 20 | 21 | [tool.isort] 22 | profile = "black" 23 | multi_line_output = 3 -------------------------------------------------------------------------------- /examples/network/.gitignore: -------------------------------------------------------------------------------- 1 | secrets.toml 2 | .preswald_deploy -------------------------------------------------------------------------------- /examples/network/README.md: -------------------------------------------------------------------------------- 1 | # Preswald Project 2 | 3 | ## Setup 4 | 1. Configure your data connections in `preswald.toml` 5 | 2. Add sensitive information (passwords, API keys) to `secrets.toml` 6 | 3. Run your app with `preswald run` 7 | 8 | ![topology](images/topology.png "Network Topology") 9 | -------------------------------------------------------------------------------- /examples/network/data.csv: -------------------------------------------------------------------------------- 1 | Device Name,IP Address,MAC Address,Connection Type,Connected To 2 | Router,192.168.1.1,00:1A:2B:3C:4D:5E,Wired,ISP 3 | Switch,192.168.1.2,00:1A:2B:3C:4D:5F,Wired,Router 4 | Desktop-PC,192.168.1.10,00:1A:2B:3C:4D:60,Wired,Switch 5 | Laptop,192.168.1.11,00:1A:2B:3C:4D:61,Wireless,Router 6 | Smartphone,192.168.1.12,00:1A:2B:3C:4D:62,Wireless,Router 7 | Smart-TV,192.168.1.13,00:1A:2B:3C:4D:63,Wireless,Router 8 | NAS,192.168.1.14,00:1A:2B:3C:4D:64,Wired,Switch 9 | Printer,192.168.1.15,00:1A:2B:3C:4D:65,Wireless,Router 10 | IP-Camera-1,192.168.1.16,00:1A:2B:3C:4D:66,Wireless,Router 11 | IP-Camera-2,192.168.1.17,00:1A:2B:3C:4D:67,Wired,Switch 12 | -------------------------------------------------------------------------------- /examples/network/hello.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import plotly.graph_objects as go 3 | 4 | from preswald import plotly, table, text 5 | 6 | 7 | text("# Network Topology Visualizer") 8 | text("Visualize the topology of a network.") 9 | 10 | df = pd.read_csv("data.csv") 11 | table(df) 12 | 13 | # Create a list of devices 14 | devices = df["Device Name"].unique() 15 | 16 | # Generate device positions programmatically (spread along x and y) 17 | device_positions = {} 18 | y_offset = 1.25 # initial y offset for device positioning 19 | x_offset = 0.0 # initial x offset for device positioning 20 | layer_spacing = 2 # spacing between layers 21 | 22 | # Place devices along the x-axis with layers for the connections 23 | for i, device in enumerate(devices): 24 | device_positions[device] = ( 25 | x_offset, 26 | y_offset - (0.25 if x_offset % 4 == 0 else -0.25), 27 | ) 28 | y_offset -= layer_spacing # Adjust vertical spacing between layers 29 | if i % 3 == 2: # Every 3 devices, move to the next column 30 | x_offset += 2 31 | y_offset = 1 # Reset the y-offset for new column 32 | 33 | # Create a mapping of device names to their IDs for easy indexing 34 | device_name_to_id = {device: idx for idx, device in enumerate(devices)} 35 | 36 | # Create edges from the 'Connected To' column in the CSV 37 | edges = [] 38 | for _, row in df.iterrows(): 39 | from_device = row["Device Name"] 40 | to_device = row["Connected To"] 41 | 42 | if to_device in device_name_to_id: 43 | edges.append((device_name_to_id[from_device], device_name_to_id[to_device])) 44 | 45 | # Extract node positions and labels 46 | node_x = [device_positions[device][0] for device in devices] 47 | node_y = [device_positions[device][1] for device in devices] 48 | node_labels = devices 49 | 50 | # Create edge coordinates (midpoints) 51 | edge_x = [] 52 | edge_y = [] 53 | for edge in edges: 54 | x0, y0 = device_positions[devices[edge[0]]] 55 | x1, y1 = device_positions[devices[edge[1]]] 56 | edge_x.append(x0) 57 | edge_x.append(x1) 58 | edge_y.append(y0) 59 | edge_y.append(y1) 60 | 61 | # Create Plotly figure 62 | fig = go.Figure() 63 | 64 | # Add edges 65 | fig.add_trace( 66 | go.Scatter( 67 | x=edge_x, 68 | y=edge_y, 69 | line={"width": 1, "color": "gray"}, 70 | hoverinfo="none", 71 | mode="lines", 72 | ) 73 | ) 74 | 75 | # Add nodes 76 | fig.add_trace( 77 | go.Scatter( 78 | x=node_x, 79 | y=node_y, 80 | text=node_labels, 81 | mode="markers+text", 82 | marker={ 83 | "size": 18, 84 | "color": "#00cccc", 85 | "line": {"width": 1, "color": "#006a6a"}, 86 | }, 87 | textposition="bottom center", 88 | ) 89 | ) 90 | 91 | # Layout 92 | fig.update_layout( 93 | title="Network Topology", 94 | showlegend=False, 95 | xaxis={"showgrid": False, "zeroline": False, "range": [-1, x_offset + 1]}, 96 | yaxis={"showgrid": False, "zeroline": False, "range": [-len(devices) / 3 - 1, 2]}, 97 | plot_bgcolor="white", 98 | ) 99 | 100 | plotly(fig) 101 | -------------------------------------------------------------------------------- /examples/network/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StructuredLabs/preswald/9a9015d90cdfd787cbbe236c89f13815310ddc9b/examples/network/images/favicon.ico -------------------------------------------------------------------------------- /examples/network/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StructuredLabs/preswald/9a9015d90cdfd787cbbe236c89f13815310ddc9b/examples/network/images/logo.png -------------------------------------------------------------------------------- /examples/network/images/topology.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StructuredLabs/preswald/9a9015d90cdfd787cbbe236c89f13815310ddc9b/examples/network/images/topology.png -------------------------------------------------------------------------------- /examples/network/preswald.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | title = "Network Topology | Preswald" 3 | version = "0.1.0" 4 | port = 8501 5 | entrypoint = "hello.py" 6 | slug = "network-topology" 7 | 8 | [branding] 9 | name = "Network Topology" 10 | logo = "images/logo.png" 11 | favicon = "images/favicon.ico" 12 | primaryColor = "#00cccc" 13 | 14 | [logging] 15 | level = "INFO" # Options: DEBUG, INFO, WARNING, ERROR, CRITICAL 16 | format = "%(asctime)s - %(name)s - %(levelname)s - %(message)s" 17 | -------------------------------------------------------------------------------- /examples/network/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["hatchling"] 3 | build-backend = "hatchling.build" 4 | 5 | [project] 6 | name = "network" 7 | version = "0.1.0" 8 | description = "A Preswald application" 9 | requires-python = ">=3.8" 10 | dependencies = [ 11 | "preswald==0.1.33", 12 | "duckdb==1.1.3" 13 | ] 14 | 15 | [tool.black] 16 | line-length = 88 17 | target-version = ['py38'] 18 | 19 | [tool.isort] 20 | profile = "black" 21 | multi_line_output = 3 22 | 23 | [tool.hatch.build.targets.wheel] 24 | packages = ["."] -------------------------------------------------------------------------------- /examples/user_event/.gitignore: -------------------------------------------------------------------------------- 1 | secrets.toml 2 | .preswald_deploy 3 | .env.structured 4 | -------------------------------------------------------------------------------- /examples/user_event/README.md: -------------------------------------------------------------------------------- 1 | # Preswald Project 2 | 3 | ## Setup 4 | 1. Configure your data connections in `preswald.toml` 5 | 2. Add sensitive information (passwords, API keys) to `secrets.toml` 6 | 3. Run your app with `preswald run hello.py` -------------------------------------------------------------------------------- /examples/user_event/data/events_flat.json: -------------------------------------------------------------------------------- 1 | [ 2 | {"user": "User1", "clicks": 10}, 3 | {"user": "User2", "clicks": 5}, 4 | {"user": "User3", "clicks": 8}, 5 | {"user": "User4", "clicks": 12}, 6 | {"user": "User5", "clicks": 7}, 7 | {"user": "User6", "clicks": 9}, 8 | {"user": "User7", "clicks": 4}, 9 | {"user": "User8", "clicks": 11}, 10 | {"user": "User9", "clicks": 6}, 11 | {"user": "User10", "clicks": 10}, 12 | {"user": "User11", "clicks": 3}, 13 | {"user": "User12", "clicks": 8}, 14 | {"user": "User13", "clicks": 5}, 15 | {"user": "User14", "clicks": 7}, 16 | {"user": "User15", "clicks": 9} 17 | ] 18 | 19 | -------------------------------------------------------------------------------- /examples/user_event/data/events_nested.json: -------------------------------------------------------------------------------- 1 | { 2 | "meta": { 3 | "source": "simulation", 4 | "generated_at": "2025-03-28T12:00:00Z" 5 | }, 6 | "events": [ 7 | {"user": "User1", "details": {"clicks": 10}}, 8 | {"user": "User2", "details": {"clicks": 5}}, 9 | {"user": "User3", "details": {"clicks": 8}}, 10 | {"user": "User4", "details": {"clicks": 12}}, 11 | {"user": "User5", "details": {"clicks": 7}}, 12 | {"user": "User6", "details": {"clicks": 9}}, 13 | {"user": "User7", "details": {"clicks": 4}}, 14 | {"user": "User8", "details": {"clicks": 11}}, 15 | {"user": "User9", "details": {"clicks": 6}}, 16 | {"user": "User10", "details": {"clicks": 10}}, 17 | {"user": "User11", "details": {"clicks": 3}}, 18 | {"user": "User12", "details": {"clicks": 8}}, 19 | {"user": "User13", "details": {"clicks": 5}}, 20 | {"user": "User14", "details": {"clicks": 7}}, 21 | {"user": "User15", "details": {"clicks": 9}} 22 | ] 23 | } 24 | 25 | -------------------------------------------------------------------------------- /examples/user_event/hello.py: -------------------------------------------------------------------------------- 1 | import plotly.express as px 2 | 3 | from preswald import connect, get_df, plotly, table, text 4 | 5 | 6 | text("# Welcome to Preswald!") 7 | text("This is your first app. 🎉") 8 | 9 | # Load the JSON source defined as "user_events" in preswald.toml 10 | connect() # This loads all data sources, including our nested JSON source. 11 | 12 | df = get_df("user_events") 13 | 14 | # Create a scatter plot using the flattened data. 15 | # Assuming the JSON file has been flattened to include "user" and "details.clicks" 16 | fig = px.scatter( 17 | df, 18 | x="user", 19 | y="details.clicks", 20 | text="user", 21 | title="User Events: Clicks per User", 22 | labels={"user": "User", "details.clicks": "Clicks"}, 23 | ) 24 | 25 | # Add labels for each point 26 | fig.update_traces(textposition="top center", marker={"size": 12, "color": "lightblue"}) 27 | 28 | 29 | # Style the plot 30 | fig.update_layout(template="plotly_white") 31 | 32 | # Display the plot 33 | plotly(fig) 34 | 35 | # Display the DataFrame as a table 36 | table(df) 37 | -------------------------------------------------------------------------------- /examples/user_event/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StructuredLabs/preswald/9a9015d90cdfd787cbbe236c89f13815310ddc9b/examples/user_event/images/favicon.ico -------------------------------------------------------------------------------- /examples/user_event/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StructuredLabs/preswald/9a9015d90cdfd787cbbe236c89f13815310ddc9b/examples/user_event/images/logo.png -------------------------------------------------------------------------------- /examples/user_event/preswald.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | title = "User Event" 3 | version = "0.1.0" 4 | port = 8501 5 | slug = "user-event-967617" # Required: Unique identifier for your project 6 | entrypoint = "hello.py" # Required: Main script to run when executing preswald run 7 | 8 | [branding] 9 | name = "Preswald Project" 10 | logo = "images/logo.png" 11 | favicon = "images/favicon.ico" 12 | primaryColor = "#F89613" 13 | 14 | [data.user_event] 15 | type = "json" 16 | path = "data/nested_user_events.json" 17 | record_path = "events" # for nested arrays 18 | flatten = true 19 | 20 | [logging] 21 | level = "INFO" # Options: DEBUG, INFO, WARNING, ERROR, CRITICAL 22 | format = "%(asctime)s - %(name)s - %(levelname)s - %(message)s" -------------------------------------------------------------------------------- /examples/user_event/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["hatchling"] 3 | build-backend = "hatchling.build" 4 | 5 | [project] 6 | name = "preswald-app" 7 | version = "0.1.0" 8 | description = "A Preswald application" 9 | requires-python = ">=3.8" 10 | dependencies = [ 11 | "preswald" 12 | ] 13 | 14 | [tool.hatch.build.targets.wheel] 15 | packages = ["."] 16 | 17 | [tool.black] 18 | line-length = 88 19 | target-version = ['py38'] 20 | 21 | [tool.isort] 22 | profile = "black" 23 | multi_line_output = 3 -------------------------------------------------------------------------------- /frontend/.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 | -------------------------------------------------------------------------------- /frontend/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": true, 3 | "singleQuote": true, 4 | "tabWidth": 2, 5 | "printWidth": 100, 6 | "trailingComma": "es5", 7 | "importOrder": [ 8 | "^react", 9 | "^@/components/(.*)$", 10 | "^@/(.*)$", 11 | "^[./]" 12 | ], 13 | "importOrderSeparation": true, 14 | "importOrderSortSpecifiers": true, 15 | "plugins": ["@trivago/prettier-plugin-sort-imports"] 16 | } 17 | -------------------------------------------------------------------------------- /frontend/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": "src/styles.css", 9 | "baseColor": "zinc", 10 | "cssVariables": true, 11 | "prefix": "" 12 | }, 13 | "aliases": { 14 | "components": "@/components", 15 | "utils": "@/lib/utils", 16 | "ui": "@/components/ui", 17 | "lib": "@/lib", 18 | "hooks": "@/hooks" 19 | }, 20 | "iconLibrary": "lucide" 21 | } -------------------------------------------------------------------------------- /frontend/eslint.config.js: -------------------------------------------------------------------------------- 1 | import js from '@eslint/js' 2 | import globals from 'globals' 3 | import react from 'eslint-plugin-react' 4 | import reactHooks from 'eslint-plugin-react-hooks' 5 | import reactRefresh from 'eslint-plugin-react-refresh' 6 | import importPlugin from 'eslint-plugin-import' 7 | 8 | 9 | export default [ 10 | { 11 | ignores: [ 12 | 'dist', 13 | 'node_modules', 14 | '*.config.js', 15 | ] 16 | }, 17 | { 18 | files: ['**/*.{js,jsx}'], 19 | extends: [ 20 | 'prettier' 21 | ], 22 | languageOptions: { 23 | ecmaVersion: 2020, 24 | globals: { 25 | ...globals.browser, 26 | ...globals.node, 27 | }, 28 | parserOptions: { 29 | ecmaVersion: 'latest', 30 | ecmaFeatures: { jsx: true }, 31 | sourceType: 'module', 32 | }, 33 | }, 34 | settings: { 35 | react: { version: '18.3' }, 36 | 'import/resolver': { 37 | node: { 38 | extensions: ['.js', '.jsx'], 39 | }, 40 | }, 41 | }, 42 | plugins: { 43 | react, 44 | 'react-hooks': reactHooks, 45 | 'react-refresh': reactRefresh, 46 | import: importPlugin, 47 | }, 48 | rules: { 49 | ...js.configs.recommended.rules, 50 | ...react.configs.recommended.rules, 51 | ...react.configs['jsx-runtime'].rules, 52 | ...reactHooks.configs.recommended.rules, 53 | 'react/jsx-no-target-blank': 'off', 54 | 'react-refresh/only-export-components': [ 55 | 'warn', 56 | { allowConstantExport: true }, 57 | ], 58 | 'no-unused-vars': ['error', { 59 | argsIgnorePattern: '^_', 60 | varsIgnorePattern: '^_', 61 | }], 62 | 'no-console': ['warn', { allow: ['warn', 'error'] }], 63 | // Disable ESLint's import ordering to let Prettier handle it 64 | 'import/order': 'off', 65 | ...react.configs['jsx-runtime'].rules, 66 | ...reactHooks.configs.recommended.rules, 67 | 'react/jsx-no-target-blank': 'off', 68 | 'react-refresh/only-export-components': [ 69 | 'warn', 70 | { allowConstantExport: true }, 71 | ], 72 | 'no-unused-vars': ['error', { 73 | argsIgnorePattern: '^_', 74 | varsIgnorePattern: '^_', 75 | }], 76 | 'no-console': ['warn', { allow: ['warn', 'error'] }], 77 | 'import/order': ['error', { 78 | groups: [ 79 | 'builtin', 80 | 'external', 81 | 'internal', 82 | ['parent', 'sibling'], 83 | 'index', 84 | ], 85 | 'newlines-between': 'always', 86 | pathGroups: [ 87 | { pattern: '^react', group: 'external', position: 'before' }, 88 | { pattern: '^@/components/(.*)$', group: 'internal', position: 'before' }, 89 | { pattern: '^@/(.*)$', group: 'internal' }, 90 | ], 91 | alphabetize: { 92 | order: 'asc', 93 | caseInsensitive: true, 94 | }, 95 | }], 96 | }, 97 | }, 98 | ] -------------------------------------------------------------------------------- /frontend/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React 8 | 9 | 10 | 11 | 12 | 13 |
14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "frontend", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "watch": "nodemon --watch src --ext js,mjs,cjs,ts,tsx,jsx --exec 'npm run build'", 10 | "preview": "vite preview", 11 | "lint": "eslint .", 12 | "lint:fix": "eslint . --fix", 13 | "format": "prettier --write '**/*.{js,jsx,css,scss,json}'" 14 | }, 15 | "dependencies": { 16 | "@ag-grid-community/client-side-row-model": "^32.3.4", 17 | "@ag-grid-community/core": "^32.3.4", 18 | "@headlessui/react": "^2.2.0", 19 | "@heroicons/react": "^2.2.0", 20 | "@monaco-editor/react": "^4.7.0", 21 | "@msgpack/msgpack": "^3.1.1", 22 | "@radix-ui/react-alert-dialog": "^1.1.4", 23 | "@radix-ui/react-checkbox": "^1.1.3", 24 | "@radix-ui/react-collapsible": "^1.1.2", 25 | "@radix-ui/react-dialog": "^1.1.4", 26 | "@radix-ui/react-hover-card": "^1.1.4", 27 | "@radix-ui/react-icons": "^1.3.2", 28 | "@radix-ui/react-label": "^2.1.1", 29 | "@radix-ui/react-progress": "^1.1.1", 30 | "@radix-ui/react-scroll-area": "^1.2.2", 31 | "@radix-ui/react-select": "^2.1.4", 32 | "@radix-ui/react-separator": "^1.1.1", 33 | "@radix-ui/react-slider": "^1.2.2", 34 | "@radix-ui/react-slot": "^1.1.1", 35 | "@radix-ui/react-tooltip": "^1.1.6", 36 | "@shadcn/ui": "^0.0.4", 37 | "@tailwindcss/typography": "^0.5.10", 38 | "@types/dagre": "^0.7.52", 39 | "ag-grid-react": "^32.3.4", 40 | "class-variance-authority": "^0.7.1", 41 | "classnames": "^2.5.1", 42 | "clsx": "^2.1.1", 43 | "comlink": "^4.4.2", 44 | "dagre": "^0.8.5", 45 | "katex": "^0.16.21", 46 | "lucide-react": "^0.473.0", 47 | "pako": "^2.1.0", 48 | "plotly.js": "^2.35.3", 49 | "plotly.js-dist": "^3.0.1", 50 | "react": "^18.3.1", 51 | "react-dom": "^18.3.1", 52 | "react-icons": "^5.4.0", 53 | "react-intersection-observer": "^9.14.1", 54 | "react-json-tree": "^0.20.0", 55 | "react-markdown": "^9.0.1", 56 | "react-plotly.js": "^2.6.0", 57 | "react-router-dom": "^6.28.1", 58 | "react-syntax-highlighter": "^15.6.1", 59 | "reactflow": "^11.11.4", 60 | "rehype-katex": "^7.0.1", 61 | "remark-autolink-headings": "^7.0.1", 62 | "remark-gfm": "^4.0.0", 63 | "remark-math": "^6.0.0", 64 | "remark-slug": "^7.0.1", 65 | "tailwind-merge": "^2.6.0", 66 | "tailwindcss-animate": "^1.0.7", 67 | "use-debounce": "^10.0.4" 68 | }, 69 | "devDependencies": { 70 | "@eslint/js": "^9.19.0", 71 | "@trivago/prettier-plugin-sort-imports": "^5.2.2", 72 | "@types/node": "^22.10.7", 73 | "@types/node-fetch": "^2.6.12", 74 | "@types/react": "^18.3.17", 75 | "@types/react-dom": "^18.3.5", 76 | "@vitejs/plugin-react": "^4.3.4", 77 | "autoprefixer": "^10.4.20", 78 | "eslint": "^9.19.0", 79 | "eslint-config-prettier": "^10.1.1", 80 | "eslint-plugin-import": "^2.31.0", 81 | "eslint-plugin-react": "^7.37.4", 82 | "eslint-plugin-react-hooks": "^5.1.0", 83 | "eslint-plugin-react-refresh": "^0.4.18", 84 | "globals": "^15.13.0", 85 | "nodemon": "^3.1.9", 86 | "postcss": "^8.5.1", 87 | "prettier": "^3.4.2", 88 | "tailwindcss": "^3.4.17", 89 | "vite": "^6.0.3" 90 | }, 91 | "vite": { 92 | "build": { 93 | "rollupOptions": { 94 | "external": [] 95 | } 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /frontend/postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /frontend/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StructuredLabs/preswald/9a9015d90cdfd787cbbe236c89f13815310ddc9b/frontend/public/favicon.ico -------------------------------------------------------------------------------- /frontend/public/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StructuredLabs/preswald/9a9015d90cdfd787cbbe236c89f13815310ddc9b/frontend/public/logo.png -------------------------------------------------------------------------------- /frontend/src/App.css: -------------------------------------------------------------------------------- 1 | #root { 2 | width: 100%; 3 | margin: 0; 4 | padding: 0; 5 | } 6 | 7 | .logo { 8 | height: 6em; 9 | padding: 1.5em; 10 | will-change: filter; 11 | transition: filter 300ms; 12 | } 13 | .logo:hover { 14 | filter: drop-shadow(0 0 2em #646cffaa); 15 | } 16 | .logo.react:hover { 17 | filter: drop-shadow(0 0 2em #61dafbaa); 18 | } 19 | 20 | @keyframes logo-spin { 21 | from { 22 | transform: rotate(0deg); 23 | } 24 | to { 25 | transform: rotate(360deg); 26 | } 27 | } 28 | 29 | @media (prefers-reduced-motion: no-preference) { 30 | a:nth-of-type(2) .logo { 31 | animation: logo-spin infinite 20s linear; 32 | } 33 | } 34 | 35 | .card { 36 | padding: 2em; 37 | } 38 | 39 | .read-the-docs { 40 | color: #888; 41 | } 42 | -------------------------------------------------------------------------------- /frontend/src/backend/service.js: -------------------------------------------------------------------------------- 1 | import * as Comlink from 'comlink'; 2 | 3 | // import PreswaldWorker from './worker.js?worker&inline'; // ← change 4 | 5 | let workerInstance = null; 6 | 7 | export function createWorker() { 8 | // If we're already initialized, return the existing worker 9 | if (workerInstance) { 10 | console.log('[Service] Reusing existing worker instance'); 11 | return workerInstance; 12 | } 13 | 14 | console.log('[Service] Starting new worker initialization'); 15 | try { 16 | const worker = new Worker(new URL('./worker.js', import.meta.url), { type: 'module' }); 17 | // const worker = new PreswaldWorker(); // ← no URL needed 18 | workerInstance = Comlink.wrap(worker); 19 | return workerInstance; 20 | } catch (error) { 21 | console.error('[Service] Worker initialization failed:', error); 22 | workerInstance = null; 23 | throw error; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /frontend/src/components/Content.jsx: -------------------------------------------------------------------------------- 1 | const Content = ({ children }) => { 2 | return
{children}
; 3 | }; 4 | 5 | export default Content; 6 | -------------------------------------------------------------------------------- /frontend/src/components/Layout.jsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import React, { useState } from 'react'; 4 | import { useEffect } from 'react'; 5 | 6 | import { PreswaldBadge } from './PreswaldBadge'; 7 | 8 | export default function Layout({ branding, children }) { 9 | const [faviconLoaded, setFaviconLoaded] = useState(false); 10 | 11 | useEffect(() => { 12 | // Get branding from window object (set by server) 13 | if (window.PRESWALD_BRANDING) { 14 | console.log('Received branding:', window.PRESWALD_BRANDING); 15 | 16 | // Update document title 17 | document.title = window.PRESWALD_BRANDING.name; 18 | 19 | // Update favicon links 20 | const updateFaviconLinks = (faviconUrl) => { 21 | // Remove any existing favicon links 22 | const existingLinks = document.querySelectorAll("link[rel*='icon']"); 23 | existingLinks.forEach((link) => link.remove()); 24 | 25 | // Create new favicon links 26 | const iconLink = document.createElement('link'); 27 | iconLink.type = 'image/x-icon'; 28 | iconLink.rel = 'icon'; 29 | iconLink.href = `${faviconUrl}?timestamp=${new Date().getTime()}`; 30 | document.head.appendChild(iconLink); 31 | 32 | const shortcutLink = document.createElement('link'); 33 | shortcutLink.type = 'image/x-icon'; 34 | shortcutLink.rel = 'shortcut icon'; 35 | shortcutLink.href = `${faviconUrl}?timestamp=${new Date().getTime()}`; 36 | document.head.appendChild(shortcutLink); 37 | }; 38 | 39 | // Function to check if favicon is accessible 40 | const checkFavicon = () => { 41 | fetch(window.PRESWALD_BRANDING.favicon) 42 | .then((response) => { 43 | if (response.ok) { 44 | setFaviconLoaded(true); 45 | console.log('Favicon loaded successfully'); 46 | updateFaviconLinks(window.PRESWALD_BRANDING.favicon); 47 | } else { 48 | throw new Error('Favicon not found'); 49 | } 50 | }) 51 | .catch((error) => { 52 | console.warn('Favicon failed to load, retrying in 1 second...', error); 53 | setTimeout(checkFavicon, 1000); 54 | }); 55 | }; 56 | 57 | checkFavicon(); 58 | } else { 59 | console.warn('No PRESWALD_BRANDING found in window object'); 60 | } 61 | }, []); 62 | 63 | return ( 64 |
65 | {window === window.top && } 66 |
67 |
68 |
{children}
69 |
70 |
71 |
72 | ); 73 | } 74 | -------------------------------------------------------------------------------- /frontend/src/components/PreswaldBadge.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { Badge } from '@/components/ui/badge'; 4 | 5 | export function PreswaldBadge() { 6 | return ( 7 | 13 | 14 | Built with Preswald 15 | 16 | 17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /frontend/src/components/TableOfContents.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react'; 2 | 3 | const TableOfContents = () => { 4 | const [headers, setHeaders] = useState([]); 5 | 6 | useEffect(() => { 7 | const contentArea = document.querySelector('.dashboard-container'); 8 | if (!contentArea) return; 9 | 10 | // Get all h1, h2, and h3 elements 11 | const headerElements = contentArea.querySelectorAll('h1, h2, h3'); 12 | const headerList = Array.from(headerElements).map((header) => { 13 | if (!header.id) { 14 | header.id = header.textContent.replace(/\s+/g, '-').toLowerCase(); 15 | } 16 | return { 17 | id: header.id, 18 | text: header.textContent, 19 | level: header.tagName.toLowerCase(), 20 | }; 21 | }); 22 | 23 | setHeaders(headerList); 24 | }, []); 25 | 26 | const handleClick = (id) => { 27 | const element = document.getElementById(id); 28 | if (element) { 29 | // Scroll to the header 30 | element.scrollIntoView({ behavior: 'smooth', block: 'start' }); 31 | } 32 | }; 33 | 34 | return ( 35 |
36 | 50 |
51 | ); 52 | }; 53 | 54 | export default TableOfContents; 55 | -------------------------------------------------------------------------------- /frontend/src/components/common/Tooltip.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { HoverCard, HoverCardContent, HoverCardTrigger } from '@/components/ui/hover-card'; 4 | 5 | export const Tooltip = ({ content, children }) => { 6 | return ( 7 | 8 | 9 |
{children}
10 |
11 | 16 | {content} 17 | 18 |
19 | ); 20 | }; 21 | -------------------------------------------------------------------------------- /frontend/src/components/pages/Dashboard.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { Alert, AlertDescription } from '@/components/ui/alert'; 4 | 5 | import DynamicComponents from '../DynamicComponents'; 6 | import LoadingState from '../LoadingState'; 7 | 8 | const Dashboard = ({ components, error, handleComponentUpdate }) => { 9 | console.log('[Dashboard] Rendering with:', { components, error }); 10 | 11 | const isValidComponents = 12 | components && 13 | components.rows && 14 | Array.isArray(components.rows) && 15 | components.rows.every((row) => Array.isArray(row)); 16 | 17 | const renderContent = () => { 18 | if (error) { 19 | return ( 20 | 21 | {error} 22 | 23 | ); 24 | } 25 | 26 | if (!isValidComponents) { 27 | return ( 28 |
29 | 33 |
34 | ); 35 | } 36 | 37 | if (components.rows.length === 0) { 38 | return ( 39 |
40 |

No components to display

41 |
42 | ); 43 | } 44 | 45 | return ; 46 | }; 47 | 48 | return
{renderContent()}
; 49 | }; 50 | 51 | export default Dashboard; 52 | -------------------------------------------------------------------------------- /frontend/src/components/ui/alert.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | import { cva, type VariantProps } from "class-variance-authority" 3 | 4 | import { cn } from "@/lib/utils" 5 | 6 | const alertVariants = cva( 7 | "relative w-full rounded-lg border px-4 py-3 text-sm [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground [&>svg~*]:pl-7", 8 | { 9 | variants: { 10 | variant: { 11 | default: "bg-background text-foreground", 12 | destructive: 13 | "border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive", 14 | }, 15 | }, 16 | defaultVariants: { 17 | variant: "default", 18 | }, 19 | } 20 | ) 21 | 22 | const Alert = React.forwardRef< 23 | HTMLDivElement, 24 | React.HTMLAttributes & VariantProps 25 | >(({ className, variant, ...props }, ref) => ( 26 |
32 | )) 33 | Alert.displayName = "Alert" 34 | 35 | const AlertTitle = React.forwardRef< 36 | HTMLParagraphElement, 37 | React.HTMLAttributes 38 | >(({ className, ...props }, ref) => ( 39 |
44 | )) 45 | AlertTitle.displayName = "AlertTitle" 46 | 47 | const AlertDescription = React.forwardRef< 48 | HTMLParagraphElement, 49 | React.HTMLAttributes 50 | >(({ className, ...props }, ref) => ( 51 |
56 | )) 57 | AlertDescription.displayName = "AlertDescription" 58 | 59 | export { Alert, AlertTitle, AlertDescription } 60 | -------------------------------------------------------------------------------- /frontend/src/components/ui/badge.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | import { cva, type VariantProps } from "class-variance-authority" 3 | 4 | import { cn } from "@/lib/utils" 5 | 6 | const badgeVariants = cva( 7 | "inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2", 8 | { 9 | variants: { 10 | variant: { 11 | default: 12 | "border-transparent bg-primary text-primary-foreground shadow hover:bg-primary/80", 13 | secondary: 14 | "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80", 15 | destructive: 16 | "border-transparent bg-destructive text-destructive-foreground shadow hover:bg-destructive/80", 17 | outline: "text-foreground", 18 | }, 19 | }, 20 | defaultVariants: { 21 | variant: "default", 22 | }, 23 | } 24 | ) 25 | 26 | export interface BadgeProps 27 | extends React.HTMLAttributes, 28 | VariantProps {} 29 | 30 | function Badge({ className, variant, ...props }: BadgeProps) { 31 | return ( 32 |
33 | ) 34 | } 35 | 36 | export { Badge, badgeVariants } 37 | -------------------------------------------------------------------------------- /frontend/src/components/ui/button.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | import { Slot } from "@radix-ui/react-slot" 3 | import { cva, type VariantProps } from "class-variance-authority" 4 | 5 | import { cn } from "@/lib/utils" 6 | 7 | const buttonVariants = cva( 8 | "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0", 9 | { 10 | variants: { 11 | variant: { 12 | default: 13 | "bg-primary text-primary-foreground shadow hover:bg-primary/90", 14 | destructive: 15 | "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90", 16 | outline: 17 | "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground", 18 | secondary: 19 | "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80", 20 | ghost: "hover:bg-accent hover:text-accent-foreground", 21 | link: "text-primary underline-offset-4 hover:underline", 22 | }, 23 | size: { 24 | default: "h-9 px-4 py-2", 25 | sm: "h-8 rounded-md px-3 text-xs", 26 | lg: "h-10 rounded-md px-8", 27 | icon: "h-9 w-9", 28 | }, 29 | }, 30 | defaultVariants: { 31 | variant: "default", 32 | size: "default", 33 | }, 34 | } 35 | ) 36 | 37 | export interface ButtonProps 38 | extends React.ButtonHTMLAttributes, 39 | VariantProps { 40 | asChild?: boolean 41 | } 42 | 43 | const Button = React.forwardRef( 44 | ({ className, variant, size, asChild = false, ...props }, ref) => { 45 | const Comp = asChild ? Slot : "button" 46 | return ( 47 | 52 | ) 53 | } 54 | ) 55 | Button.displayName = "Button" 56 | 57 | export { Button, buttonVariants } 58 | -------------------------------------------------------------------------------- /frontend/src/components/ui/card.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | 3 | import { cn } from "@/lib/utils" 4 | 5 | const Card = React.forwardRef< 6 | HTMLDivElement, 7 | React.HTMLAttributes 8 | >(({ className, ...props }, ref) => ( 9 |
17 | )) 18 | Card.displayName = "Card" 19 | 20 | const CardHeader = React.forwardRef< 21 | HTMLDivElement, 22 | React.HTMLAttributes 23 | >(({ className, ...props }, ref) => ( 24 |
29 | )) 30 | CardHeader.displayName = "CardHeader" 31 | 32 | const CardTitle = React.forwardRef< 33 | HTMLDivElement, 34 | React.HTMLAttributes 35 | >(({ className, ...props }, ref) => ( 36 |
41 | )) 42 | CardTitle.displayName = "CardTitle" 43 | 44 | const CardDescription = React.forwardRef< 45 | HTMLDivElement, 46 | React.HTMLAttributes 47 | >(({ className, ...props }, ref) => ( 48 |
53 | )) 54 | CardDescription.displayName = "CardDescription" 55 | 56 | const CardContent = React.forwardRef< 57 | HTMLDivElement, 58 | React.HTMLAttributes 59 | >(({ className, ...props }, ref) => ( 60 |
61 | )) 62 | CardContent.displayName = "CardContent" 63 | 64 | const CardFooter = React.forwardRef< 65 | HTMLDivElement, 66 | React.HTMLAttributes 67 | >(({ className, ...props }, ref) => ( 68 |
73 | )) 74 | CardFooter.displayName = "CardFooter" 75 | 76 | export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent } 77 | -------------------------------------------------------------------------------- /frontend/src/components/ui/checkbox.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | import * as CheckboxPrimitive from "@radix-ui/react-checkbox" 3 | import { Check } from "lucide-react" 4 | 5 | import { cn } from "@/lib/utils" 6 | 7 | const Checkbox = React.forwardRef< 8 | React.ElementRef, 9 | React.ComponentPropsWithoutRef 10 | >(({ className, ...props }, ref) => ( 11 | 19 | 22 | 23 | 24 | 25 | )) 26 | Checkbox.displayName = CheckboxPrimitive.Root.displayName 27 | 28 | export { Checkbox } 29 | -------------------------------------------------------------------------------- /frontend/src/components/ui/collapsible.tsx: -------------------------------------------------------------------------------- 1 | import * as CollapsiblePrimitive from "@radix-ui/react-collapsible" 2 | 3 | const Collapsible = CollapsiblePrimitive.Root 4 | 5 | const CollapsibleTrigger = CollapsiblePrimitive.CollapsibleTrigger 6 | 7 | const CollapsibleContent = CollapsiblePrimitive.CollapsibleContent 8 | 9 | export { Collapsible, CollapsibleTrigger, CollapsibleContent } 10 | -------------------------------------------------------------------------------- /frontend/src/components/ui/hover-card.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as React from "react" 4 | import * as HoverCardPrimitive from "@radix-ui/react-hover-card" 5 | 6 | import { cn } from "@/lib/utils" 7 | 8 | const HoverCard = HoverCardPrimitive.Root 9 | 10 | const HoverCardTrigger = HoverCardPrimitive.Trigger 11 | 12 | const HoverCardContent = React.forwardRef< 13 | React.ElementRef, 14 | React.ComponentPropsWithoutRef 15 | >(({ className, align = "center", sideOffset = 4, ...props }, ref) => ( 16 | 26 | )) 27 | HoverCardContent.displayName = HoverCardPrimitive.Content.displayName 28 | 29 | export { HoverCard, HoverCardTrigger, HoverCardContent } 30 | -------------------------------------------------------------------------------- /frontend/src/components/ui/input.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | 3 | import { cn } from "@/lib/utils" 4 | 5 | const Input = React.forwardRef>( 6 | ({ className, type, ...props }, ref) => { 7 | return ( 8 | 17 | ) 18 | } 19 | ) 20 | Input.displayName = "Input" 21 | 22 | export { Input } 23 | -------------------------------------------------------------------------------- /frontend/src/components/ui/label.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | import * as LabelPrimitive from "@radix-ui/react-label" 3 | import { cva, type VariantProps } from "class-variance-authority" 4 | 5 | import { cn } from "@/lib/utils" 6 | 7 | const labelVariants = cva( 8 | "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70" 9 | ) 10 | 11 | const Label = React.forwardRef< 12 | React.ElementRef, 13 | React.ComponentPropsWithoutRef & 14 | VariantProps 15 | >(({ className, ...props }, ref) => ( 16 | 21 | )) 22 | Label.displayName = LabelPrimitive.Root.displayName 23 | 24 | export { Label } 25 | -------------------------------------------------------------------------------- /frontend/src/components/ui/progress.jsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | const Progress = React.forwardRef((props, ref) => { 4 | const { value = 0, ...other } = props; 5 | 6 | return ( 7 |
12 |
13 |
14 | ); 15 | }); 16 | 17 | Progress.displayName = 'Progress'; 18 | 19 | export { Progress }; 20 | -------------------------------------------------------------------------------- /frontend/src/components/ui/scroll-area.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as React from "react" 4 | import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area" 5 | 6 | import { cn } from "@/lib/utils" 7 | 8 | const ScrollArea = React.forwardRef< 9 | React.ElementRef, 10 | React.ComponentPropsWithoutRef 11 | >(({ className, children, ...props }, ref) => ( 12 | 17 | 18 | {children} 19 | 20 | 21 | 22 | 23 | )) 24 | ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName 25 | 26 | const ScrollBar = React.forwardRef< 27 | React.ElementRef, 28 | React.ComponentPropsWithoutRef 29 | >(({ className, orientation = "vertical", ...props }, ref) => ( 30 | 43 | 44 | 45 | )) 46 | ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName 47 | 48 | export { ScrollArea, ScrollBar } 49 | -------------------------------------------------------------------------------- /frontend/src/components/ui/separator.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | import * as SeparatorPrimitive from "@radix-ui/react-separator" 3 | 4 | import { cn } from "@/lib/utils" 5 | 6 | const Separator = React.forwardRef< 7 | React.ElementRef, 8 | React.ComponentPropsWithoutRef 9 | >( 10 | ( 11 | { className, orientation = "horizontal", decorative = true, ...props }, 12 | ref 13 | ) => ( 14 | 25 | ) 26 | ) 27 | Separator.displayName = SeparatorPrimitive.Root.displayName 28 | 29 | export { Separator } 30 | -------------------------------------------------------------------------------- /frontend/src/components/ui/skeleton.tsx: -------------------------------------------------------------------------------- 1 | import { cn } from "@/lib/utils" 2 | 3 | function Skeleton({ 4 | className, 5 | ...props 6 | }: React.HTMLAttributes) { 7 | return ( 8 |
12 | ) 13 | } 14 | 15 | export { Skeleton } 16 | -------------------------------------------------------------------------------- /frontend/src/components/ui/slider.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | import * as SliderPrimitive from "@radix-ui/react-slider" 3 | 4 | import { cn } from "@/lib/utils" 5 | 6 | const Slider = React.forwardRef< 7 | React.ElementRef, 8 | React.ComponentPropsWithoutRef 9 | >(({ className, ...props }, ref) => ( 10 | 18 | 19 | 20 | 21 | 22 | 23 | )) 24 | Slider.displayName = SliderPrimitive.Root.displayName 25 | 26 | export { Slider } 27 | -------------------------------------------------------------------------------- /frontend/src/components/ui/table.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | 3 | import { cn } from "@/lib/utils" 4 | 5 | const Table = React.forwardRef< 6 | HTMLTableElement, 7 | React.HTMLAttributes 8 | >(({ className, ...props }, ref) => ( 9 |
10 | 15 | 16 | )) 17 | Table.displayName = "Table" 18 | 19 | const TableHeader = React.forwardRef< 20 | HTMLTableSectionElement, 21 | React.HTMLAttributes 22 | >(({ className, ...props }, ref) => ( 23 | 24 | )) 25 | TableHeader.displayName = "TableHeader" 26 | 27 | const TableBody = React.forwardRef< 28 | HTMLTableSectionElement, 29 | React.HTMLAttributes 30 | >(({ className, ...props }, ref) => ( 31 | 36 | )) 37 | TableBody.displayName = "TableBody" 38 | 39 | const TableFooter = React.forwardRef< 40 | HTMLTableSectionElement, 41 | React.HTMLAttributes 42 | >(({ className, ...props }, ref) => ( 43 | tr]:last:border-b-0", 47 | className 48 | )} 49 | {...props} 50 | /> 51 | )) 52 | TableFooter.displayName = "TableFooter" 53 | 54 | const TableRow = React.forwardRef< 55 | HTMLTableRowElement, 56 | React.HTMLAttributes 57 | >(({ className, ...props }, ref) => ( 58 | 66 | )) 67 | TableRow.displayName = "TableRow" 68 | 69 | const TableHead = React.forwardRef< 70 | HTMLTableCellElement, 71 | React.ThHTMLAttributes 72 | >(({ className, ...props }, ref) => ( 73 |
[role=checkbox]]:translate-y-[2px]", 77 | className 78 | )} 79 | {...props} 80 | /> 81 | )) 82 | TableHead.displayName = "TableHead" 83 | 84 | const TableCell = React.forwardRef< 85 | HTMLTableCellElement, 86 | React.TdHTMLAttributes 87 | >(({ className, ...props }, ref) => ( 88 | [role=checkbox]]:translate-y-[2px]", 92 | className 93 | )} 94 | {...props} 95 | /> 96 | )) 97 | TableCell.displayName = "TableCell" 98 | 99 | const TableCaption = React.forwardRef< 100 | HTMLTableCaptionElement, 101 | React.HTMLAttributes 102 | >(({ className, ...props }, ref) => ( 103 |
108 | )) 109 | TableCaption.displayName = "TableCaption" 110 | 111 | export { 112 | Table, 113 | TableHeader, 114 | TableBody, 115 | TableFooter, 116 | TableHead, 117 | TableRow, 118 | TableCell, 119 | TableCaption, 120 | } 121 | -------------------------------------------------------------------------------- /frontend/src/components/ui/tooltip.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | import * as TooltipPrimitive from "@radix-ui/react-tooltip" 3 | 4 | import { cn } from "@/lib/utils" 5 | 6 | const TooltipProvider = TooltipPrimitive.Provider 7 | 8 | const Tooltip = TooltipPrimitive.Root 9 | 10 | const TooltipTrigger = TooltipPrimitive.Trigger 11 | 12 | const TooltipContent = React.forwardRef< 13 | React.ElementRef, 14 | React.ComponentPropsWithoutRef 15 | >(({ className, sideOffset = 4, ...props }, ref) => ( 16 | 17 | 26 | 27 | )) 28 | TooltipContent.displayName = TooltipPrimitive.Content.displayName 29 | 30 | export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } 31 | -------------------------------------------------------------------------------- /frontend/src/components/widgets/AlertWidget.jsx: -------------------------------------------------------------------------------- 1 | import { AlertTriangle, CheckCircle2, Info, XCircle } from 'lucide-react'; 2 | 3 | import React from 'react'; 4 | 5 | import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert'; 6 | 7 | import { cn } from '@/lib/utils'; 8 | 9 | const levelConfig = { 10 | success: { 11 | icon: CheckCircle2, 12 | variant: 'success', 13 | title: 'Success', 14 | }, 15 | warning: { 16 | icon: AlertTriangle, 17 | variant: 'warning', 18 | title: 'Warning', 19 | }, 20 | error: { 21 | icon: XCircle, 22 | variant: 'destructive', 23 | title: 'Error', 24 | }, 25 | info: { 26 | icon: Info, 27 | variant: 'default', 28 | title: 'Information', 29 | }, 30 | }; 31 | 32 | const AlertWidget = ({ id, message, level = 'info', className }) => { 33 | const config = levelConfig[level] || levelConfig.info; 34 | const Icon = config.icon; 35 | 36 | return ( 37 | 38 | 39 | {config.title} 40 | {message} 41 | 42 | ); 43 | }; 44 | 45 | export default AlertWidget; 46 | -------------------------------------------------------------------------------- /frontend/src/components/widgets/BigNumberWidget.jsx: -------------------------------------------------------------------------------- 1 | import { ArrowDown, ArrowUp } from 'lucide-react'; 2 | 3 | import React from 'react'; 4 | 5 | // Utility to format large numbers 6 | const formatNumber = (num) => { 7 | if (Math.abs(num) >= 1e9) return (num / 1e9).toFixed(1) + 'B'; 8 | if (Math.abs(num) >= 1e6) return (num / 1e6).toFixed(1) + 'M'; 9 | if (Math.abs(num) >= 1e3) return (num / 1e3).toFixed(1) + 'K'; 10 | return num; 11 | }; 12 | 13 | const BigNumberCard = ({ id, label, value, delta, unit }) => { 14 | const deltaNumber = parseFloat(delta); 15 | const isPositive = deltaNumber >= 0; 16 | 17 | const displayDelta = 18 | typeof delta === 'string' ? delta : `${isPositive ? '+' : ''}${delta}${unit ?? ''}`; 19 | 20 | return ( 21 |
22 |
{label}
23 |
24 | {formatNumber(value)} 25 | {unit ?? ''} 26 |
27 | {delta !== undefined && ( 28 |
33 | {isPositive ? : } 34 | {displayDelta} 35 |
36 | )} 37 |
38 | ); 39 | }; 40 | 41 | export default BigNumberCard; 42 | -------------------------------------------------------------------------------- /frontend/src/components/widgets/ButtonWidget.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { Button } from '@/components/ui/button'; 4 | 5 | import { cn } from '@/lib/utils'; 6 | 7 | const ButtonWidget = ({ 8 | id, 9 | children, 10 | onClick, 11 | isLoading, 12 | disabled, 13 | variant = 'default', 14 | size = 'default', 15 | className, 16 | ...props 17 | }) => { 18 | return ( 19 | 37 | ); 38 | }; 39 | 40 | export default ButtonWidget; 41 | -------------------------------------------------------------------------------- /frontend/src/components/widgets/CheckboxWidget.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { Checkbox } from '@/components/ui/checkbox'; 4 | import { Label } from '@/components/ui/label'; 5 | 6 | import { cn } from '@/lib/utils'; 7 | 8 | const CheckboxWidget = ({ 9 | label, 10 | checked = false, 11 | description, 12 | id, 13 | onChange, 14 | className, 15 | disabled = false, 16 | }) => { 17 | const handleCheckedChange = (checked) => { 18 | console.log('[CheckboxWidget] Change event:', { 19 | id, 20 | oldValue: checked, 21 | newValue: checked, 22 | timestamp: new Date().toISOString(), 23 | }); 24 | 25 | try { 26 | onChange?.(checked); 27 | console.log('[CheckboxWidget] State updated successfully:', { 28 | id, 29 | value: checked, 30 | }); 31 | } catch (error) { 32 | console.error('[CheckboxWidget] Error updating state:', { 33 | id, 34 | error: error.message, 35 | }); 36 | } 37 | }; 38 | 39 | return ( 40 |
41 | 47 |
48 | 51 | {description &&

{description}

} 52 |
53 |
54 | ); 55 | }; 56 | 57 | export default CheckboxWidget; 58 | -------------------------------------------------------------------------------- /frontend/src/components/widgets/FastplotlibWidget.jsx: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | 3 | import React, { useEffect, useState } from 'react'; 4 | 5 | import { Card } from '@/components/ui/card'; 6 | 7 | import { cn } from '@/lib/utils'; 8 | import { comm } from '@/utils/websocket'; 9 | 10 | const FastplotlibWidget = ({ id, label, src, className, clientId }) => { 11 | const [currentSrc, setCurrentSrc] = useState(src); 12 | const [hasLoadedOnce, setHasLoadedOnce] = useState(!!src); 13 | const [showWarning, setShowWarning] = useState(false); 14 | 15 | useEffect(() => { 16 | if (clientId) { 17 | comm.updateComponentState('client_id', clientId); 18 | } 19 | }, [clientId]); 20 | 21 | useEffect(() => { 22 | const unsubscribe = comm.subscribe((message) => { 23 | if (message.type === 'image_update' && message.component_id === id) { 24 | if (message.value) { 25 | setCurrentSrc(message.value); 26 | setHasLoadedOnce(true); 27 | setShowWarning(false); // reset warning on valid data 28 | } else { 29 | console.warn(`[FastplotlibWidget:${id}] image update received without data.`); 30 | setShowWarning(true); // show warning if data missing 31 | } 32 | } 33 | }); 34 | 35 | return () => unsubscribe(); 36 | }, [id]); 37 | 38 | return ( 39 | 40 | {hasLoadedOnce ? ( 41 | <> 42 | {label 43 | {showWarning && ( 44 |
45 | Warning: Latest update did not include data. 46 |
47 | )} 48 | 49 | ) : ( 50 |
Loading...
51 | )} 52 |
53 | ); 54 | }; 55 | 56 | FastplotlibWidget.propTypes = { 57 | id: PropTypes.string.isRequired, 58 | label: PropTypes.string, 59 | src: PropTypes.string, 60 | className: PropTypes.string, 61 | clientId: PropTypes.string, 62 | }; 63 | 64 | export default React.memo(FastplotlibWidget); 65 | -------------------------------------------------------------------------------- /frontend/src/components/widgets/GenericWidget.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const GenericWidget = ({ id, value, mimetype = 'text/plain' }) => { 4 | const cleanMime = mimetype.split(';')[0].trim().toLowerCase(); 5 | 6 | const renderContent = () => { 7 | if (!value) { 8 | return
No content to display.
; 9 | } 10 | 11 | if (cleanMime.startsWith('image/')) { 12 | return ( 13 | rendered image 18 | ); 19 | } 20 | 21 | if (cleanMime === 'text/html') { 22 | return ( 23 |
27 | ); 28 | } 29 | 30 | if (cleanMime === 'application/pdf') { 31 | return ( 32 |