From 58ba290bdaef239e89df8661dd139c5d665d950e Mon Sep 17 00:00:00 2001 From: europanite Date: Wed, 22 Oct 2025 11:12:05 +0900 Subject: [PATCH 1/2] renamed the repository --- .gitignore | 2 +- README.md | 10 +- SECURITY.md | 4 +- _config.yml | 4 +- frontend/app/app.json | 2 +- frontend/app/public/404.html | 4 +- frontend/app/screens/HomeScreen.tsx | 2 +- python_front_20251020_012446.md | 2214 +++++++++++++++++++++++++++ python_front_20251020_012533.md | 2068 +++++++++++++++++++++++++ python_front_20251020_013059.md | 2148 ++++++++++++++++++++++++++ python_front_20251020_013705.md | 2200 ++++++++++++++++++++++++++ 11 files changed, 8644 insertions(+), 14 deletions(-) create mode 100644 python_front_20251020_012446.md create mode 100644 python_front_20251020_012533.md create mode 100644 python_front_20251020_013059.md create mode 100644 python_front_20251020_013705.md diff --git a/.gitignore b/.gitignore index 1ab30b6..799f6d9 100644 --- a/.gitignore +++ b/.gitignore @@ -55,4 +55,4 @@ frontend/app/coverage/ frontend/app/.jest-cache/ make_md.py -python_front_* \ No newline at end of file +browser_based_python_* \ No newline at end of file diff --git a/README.md b/README.md index 0796ca0..3a895b0 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,15 @@ -# [Python Front](https://github.com/europanite/python_front "Python Front") +# [Browser Based Python](https://github.com/europanite/browser_based_python "Browser Based Python") -[![CI](https://github.com/europanite/python_front/actions/workflows/ci.yml/badge.svg)](https://github.com/europanite/python_front/actions/workflows/ci.yml) -[![Frontend Tests via Docker](https://github.com/europanite/python_front/actions/workflows/docker.yml/badge.svg)](https://github.com/europanite/python_front/actions/workflows/docker.yml) -[![Deploy Expo Web to GitHub Pages](https://github.com/europanite/python_front/actions/workflows/deploy-pages.yml/badge.svg)](https://github.com/europanite/python_front/actions/workflows/deploy-pages.yml) +[![CI](https://github.com/europanite/browser_based_python/actions/workflows/ci.yml/badge.svg)](https://github.com/europanite/browser_based_python/actions/workflows/ci.yml) +[![Frontend Tests via Docker](https://github.com/europanite/browser_based_python/actions/workflows/docker.yml/badge.svg)](https://github.com/europanite/browser_based_python/actions/workflows/docker.yml) +[![Deploy Expo Web to GitHub Pages](https://github.com/europanite/browser_based_python/actions/workflows/deploy-pages.yml/badge.svg)](https://github.com/europanite/browser_based_python/actions/workflows/deploy-pages.yml) A browser based Python playground. !["web_ui"](./assets/images/web_ui.png) ## Demo - [Python Front](https://europanite.github.io/python_front/) + [Python Front](https://europanite.github.io/browser_based_python/) --- diff --git a/SECURITY.md b/SECURITY.md index 66e024c..eec3376 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -2,7 +2,7 @@ ## Supported Versions -The following table shows which versions of `python_front` are currently being supported with security updates. +The following table shows which versions of `browser_based_python` are currently being supported with security updates. | Version | Supported | |---------|--------------------| @@ -35,4 +35,4 @@ If you discover a security vulnerability within this project, please help us kee ## Acknowledgements -We deeply appreciate the efforts of security researchers and contributors who help us improve the security of `python_front`. +We deeply appreciate the efforts of security researchers and contributors who help us improve the security of `browser_based_python`. diff --git a/_config.yml b/_config.yml index c352d93..ae96746 100644 --- a/_config.yml +++ b/_config.yml @@ -1,6 +1,6 @@ -title: "python_front" +title: "browser_based_python" description: "A playground for Python" -baseurl: "/python_front" +baseurl: "/browser_based_python" url: "https://europanite.github.io" theme: minima markdown: kramdown diff --git a/frontend/app/app.json b/frontend/app/app.json index 56a2820..8a37079 100644 --- a/frontend/app/app.json +++ b/frontend/app/app.json @@ -23,7 +23,7 @@ "edgeToEdgeEnabled": true }, "experiments": { - "baseUrl": "/python_front" + "baseUrl": "/browser_based_python" }, "web": { "favicon": "./assets/favicon.png", diff --git a/frontend/app/public/404.html b/frontend/app/public/404.html index 9909e84..1d74d9c 100644 --- a/frontend/app/public/404.html +++ b/frontend/app/public/404.html @@ -2,10 +2,10 @@ Redirecting... diff --git a/frontend/app/screens/HomeScreen.tsx b/frontend/app/screens/HomeScreen.tsx index e32f54f..91d02c3 100644 --- a/frontend/app/screens/HomeScreen.tsx +++ b/frontend/app/screens/HomeScreen.tsx @@ -206,7 +206,7 @@ export default function HomeScreen() {

+ +# Project Export: python_front + +## Overview + +- Root: `/home/skinner/python_front` +- Files: **40** +- Total size: **47018 bytes** +- Total LOC: 1433 | SLOC: 1217 | TODOs: 0 + +### Language mix +- tsx: 8 +- plain: 6 +- markdown: 6 +- yaml: 6 +- javascript: 6 +- typescript: 3 +- json: 2 +- html: 2 +- dockerfile: 1 + +### Top 12 largest files (bytes) +- `LICENSE` — 11340 bytes +- `frontend/app/screens/HomeScreen.tsx` — 8293 bytes +- `CODE_OF_CONDUCT.md` — 4085 bytes +- `.github/workflows/frontend.yml` — 2348 bytes +- `frontend/app/components/SettingsBar.tsx` — 1656 bytes +- `frontend/app/__tests__/home.screen.test.tsx` — 1421 bytes +- `.github/workflows/deploy-pages.yml` — 1203 bytes +- `SECURITY.md` — 1161 bytes +- `frontend/app/jest.config.ts` — 1011 bytes +- `frontend/app/context/Auth.tsx` — 1001 bytes +- `frontend/app/App.tsx` — 991 bytes +- `frontend/app/__tests__/settingsbar.test.tsx` — 892 bytes + +### Top 12 longest files (LOC) +- `frontend/app/screens/HomeScreen.tsx` — 271 LOC +- `LICENSE` — 201 LOC +- `.github/workflows/frontend.yml` — 94 LOC +- `CODE_OF_CONDUCT.md` — 68 LOC +- `.gitignore` — 58 LOC +- `frontend/app/components/SettingsBar.tsx` — 51 LOC +- `.github/workflows/deploy-pages.yml` — 47 LOC +- `SECURITY.md` — 38 LOC +- `frontend/app/.gitignore` — 37 LOC +- `frontend/app/__tests__/home.screen.test.tsx` — 37 LOC +- `frontend/app/context/Auth.tsx` — 35 LOC +- `.dockerignore` — 34 LOC + +### Project tree (included subset) +``` +python_front/ +├── .github/ +│ ├── ISSUE_TEMPLATE/ +│ │ └── bug_report.md +│ ├── workflows/ +│ │ ├── deploy-pages.yml +│ │ ├── docker.yml +│ │ └── frontend.yml +│ └── pull_request_template.md +├── frontend/ +│ ├── app/ +│ │ ├── __mocks__/ +│ │ │ ├── expoConstantsMock.js +│ │ │ ├── expoMock.js +│ │ │ └── expoRouterMock.js +│ │ ├── __tests__/ +│ │ │ ├── auth.context.test.tsx +│ │ │ ├── home.screen.test.tsx +│ │ │ ├── settingsbar.test.tsx +│ │ │ └── smoke.test.tsx +│ │ ├── components/ +│ │ │ └── SettingsBar.tsx +│ │ ├── context/ +│ │ │ └── Auth.tsx +│ │ ├── public/ +│ │ │ ├── 404.html +│ │ │ └── google095bf08db4fb15d0.html +│ │ ├── screens/ +│ │ │ └── HomeScreen.tsx +│ │ ├── .gitignore +│ │ ├── app.json +│ │ ├── App.tsx +│ │ ├── babel.config.js +│ │ ├── index.ts +│ │ ├── jest.config.ts +│ │ ├── jest.setup.ts +│ │ ├── jest.setupFiles.js +│ │ ├── metro.config.js +│ │ └── tsconfig.json +│ ├── Dockerfile +│ └── Dockerfile.test +├── .dockerignore +├── .env +├── .gitignore +├── _config.yml +├── CODE_OF_CONDUCT.md +├── CONTRIBUTING.md +├── docker-compose.test.yml +├── docker-compose.yml +├── LICENSE +├── README.md +└── SECURITY.md +``` + +## Table of contents (files) + +- 1. [.dockerignore](#.dockerignore) +- 2. [.env](#.env) +- 3. [.github/ISSUE_TEMPLATE/bug_report.md](#.github-ISSUE_TEMPLATE-bug_report.md) +- 4. [.github/pull_request_template.md](#.github-pull_request_template.md) +- 5. [.github/workflows/deploy-pages.yml](#.github-workflows-deploy-pages.yml) +- 6. [.github/workflows/docker.yml](#.github-workflows-docker.yml) +- 7. [.github/workflows/frontend.yml](#.github-workflows-frontend.yml) +- 8. [.gitignore](#.gitignore) +- 9. [_config.yml](#_config.yml) +- 10. [CODE_OF_CONDUCT.md](#CODE_OF_CONDUCT.md) +- 11. [CONTRIBUTING.md](#CONTRIBUTING.md) +- 12. [docker-compose.test.yml](#docker-compose.test.yml) +- 13. [docker-compose.yml](#docker-compose.yml) +- 14. [frontend/app/.gitignore](#frontend-app-.gitignore) +- 15. [frontend/app/__mocks__/expoConstantsMock.js](#frontend-app-__mocks__-expoConstantsMock.js) +- 16. [frontend/app/__mocks__/expoMock.js](#frontend-app-__mocks__-expoMock.js) +- 17. [frontend/app/__mocks__/expoRouterMock.js](#frontend-app-__mocks__-expoRouterMock.js) +- 18. [frontend/app/__tests__/auth.context.test.tsx](#frontend-app-__tests__-auth.context.test.tsx) +- 19. [frontend/app/__tests__/home.screen.test.tsx](#frontend-app-__tests__-home.screen.test.tsx) +- 20. [frontend/app/__tests__/settingsbar.test.tsx](#frontend-app-__tests__-settingsbar.test.tsx) +- 21. [frontend/app/__tests__/smoke.test.tsx](#frontend-app-__tests__-smoke.test.tsx) +- 22. [frontend/app/app.json](#frontend-app-app.json) +- 23. [frontend/app/App.tsx](#frontend-app-App.tsx) +- 24. [frontend/app/babel.config.js](#frontend-app-babel.config.js) +- 25. [frontend/app/components/SettingsBar.tsx](#frontend-app-components-SettingsBar.tsx) +- 26. [frontend/app/context/Auth.tsx](#frontend-app-context-Auth.tsx) +- 27. [frontend/app/index.ts](#frontend-app-index.ts) +- 28. [frontend/app/jest.config.ts](#frontend-app-jest.config.ts) +- 29. [frontend/app/jest.setup.ts](#frontend-app-jest.setup.ts) +- 30. [frontend/app/jest.setupFiles.js](#frontend-app-jest.setupFiles.js) +- 31. [frontend/app/metro.config.js](#frontend-app-metro.config.js) +- 32. [frontend/app/public/404.html](#frontend-app-public-404.html) +- 33. [frontend/app/public/google095bf08db4fb15d0.html](#frontend-app-public-google095bf08db4fb15d0.html) +- 34. [frontend/app/screens/HomeScreen.tsx](#frontend-app-screens-HomeScreen.tsx) +- 35. [frontend/app/tsconfig.json](#frontend-app-tsconfig.json) +- 36. [frontend/Dockerfile](#frontend-Dockerfile) +- 37. [frontend/Dockerfile.test](#frontend-Dockerfile.test) +- 38. [LICENSE](#LICENSE) +- 39. [README.md](#README.md) +- 40. [SECURITY.md](#SECURITY.md) + +--- + +## Files + + +### 1. `.dockerignore` +- Size: 305 bytes | LOC: 34 | SLOC: 29 | TODOs: 0 | Modified: 2025-10-19 20:28:35 | SHA1: 4ddfad028bab + +#### Brief +# Git +.git + +#### Auto Summary +# Git + +#### Content + +``` +# Git +.git +.gitignore + +# Python cache +__pycache__/ +*.py[cod] +*.pyo +*.pyd + +# Node +node_modules/ +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Local env/config +.env +*.env +*.local + +# OS / IDE +.DS_Store +Thumbs.db +desktop.ini +.vscode/ +.idea/ + +# Build artifacts +dist/ +build/ +web-build/ +.expo/ +.expo-shared/ +``` + + +### 2. `.env` +- Size: 230 bytes | LOC: 6 | SLOC: 6 | TODOs: 0 | Modified: 2025-10-19 20:28:35 | SHA1: ee13beb2355e + +#### Brief +# Frontend (Expo) +EXPO_TUNNEL=false + +#### Auto Summary +# Frontend (Expo) + +#### Content + +``` +# Frontend (Expo) +EXPO_TUNNEL=false +EXPO_DEVTOOLS_LISTEN_ADDRESS=0.0.0.0 +EXPO_PUBLIC_API_HOST=${REACT_NATIVE_PACKAGER_HOSTNAME} +EXPO_PUBLIC_API_PORT=8000 +EXPO_PUBLIC_API_BASE=http://${EXPO_PUBLIC_API_HOST}:${EXPO_PUBLIC_API_PORT} +``` + + +### 3. `.github/ISSUE_TEMPLATE/bug_report.md` +- Size: 666 bytes | LOC: 30 | SLOC: 24 | TODOs: 0 | Modified: 2025-10-19 20:28:35 | SHA1: 4b4d2b96744a + +#### Brief +--- +name: Bug Report + +#### Auto Summary +Description + +#### Content (verbatim) + +```markdown +--- +name: Bug Report +about: Create a report to help us improve +title: "[Bug] " +labels: bug +assignees: '' +--- + +## Description + + +## Steps to Reproduce +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +## Expected Behavior + + +## Screenshots + + +## Environment +- OS: [e.g. Ubuntu 22.04] +- Browser/Version: [e.g. Chrome 117] +- Node/Python Version: [e.g. Node 18, Python 3.10] + +## Additional Context + +``` + + +### 4. `.github/pull_request_template.md` +- Size: 744 bytes | LOC: 27 | SLOC: 21 | TODOs: 0 | Modified: 2025-10-19 20:28:35 | SHA1: 1b6e1aab4d9a + +#### Brief +# Pull Request + +#### Auto Summary +Pull Request + +#### Content (verbatim) + +```markdown +# Pull Request + +## Overview + + +## Changes + +- + +## Testing + +- [ ] Built and ran locally without errors +- [ ] All tests passed +- [ ] Verified functionality manually (describe how) + +## Related Issues + +- Closes # + +## Checklist +- [ ] Code is clean and free of unnecessary comments/debug prints +- [ ] Proper naming conventions and documentation are followed +- [ ] Updated documentation/README if necessary +- [ ] CI pipeline passes successfully + +## Notes for Reviewers + +``` + + +### 5. `.github/workflows/deploy-pages.yml` +- Size: 1203 bytes | LOC: 47 | SLOC: 47 | TODOs: 0 | Modified: 2025-10-19 20:28:35 | SHA1: d8ecc8534c1d + +#### Brief +name: Deploy Expo Web to GitHub Pages +on: + +#### Auto Summary +name: Deploy Expo Web to GitHub Pages + +#### Content + +```yaml +name: Deploy Expo Web to GitHub Pages +on: + push: + branches: [ "main" ] + workflow_dispatch: +permissions: + contents: read + pages: write + id-token: write +concurrency: + group: "pages" + cancel-in-progress: true +jobs: + build: + if: github.ref == 'refs/heads/main' + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Use Node.js 20 + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + cache-dependency-path: | + frontend/app/package-lock.json + - name: Install deps + working-directory: frontend/app + run: npm ci + - name: Build (expo export -p web) + working-directory: frontend/app + run: npx expo export -p web + - name: Upload Pages artifact + uses: actions/upload-pages-artifact@v3 + with: + path: frontend/app/dist + deploy: + if: github.ref == 'refs/heads/main' + needs: build + runs-on: ubuntu-latest + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 +``` + + +### 6. `.github/workflows/docker.yml` +- Size: 807 bytes | LOC: 27 | SLOC: 27 | TODOs: 0 | Modified: 2025-10-19 20:28:35 | SHA1: 8370695b3ad1 + +#### Brief +name: Frontend Tests via Docker +on: + +#### Auto Summary +name: Frontend Tests via Docker + +#### Content + +```yaml +name: Frontend Tests via Docker +on: + workflow_dispatch: + pull_request: +jobs: + docker-tests: + runs-on: ubuntu-latest + env: + CI: "1" + EXPO_TUNNEL: "false" + EXPO_DEVTOOLS_LISTEN_ADDRESS: 0.0.0.0 + EXPO_PUBLIC_API_BASE: http://localhost:8000 + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - name: Run tests via docker-compose (deterministic CI) + run: | + docker compose -f docker-compose.test.yml up --build --exit-code-from frontend_test + - name: Upload test reports (if mounted) + if: always() + uses: actions/upload-artifact@v4 + with: + name: frontend-reports + path: reports/frontend + if-no-files-found: warn +``` + + +### 7. `.github/workflows/frontend.yml` +- Size: 2348 bytes | LOC: 94 | SLOC: 78 | TODOs: 0 | Modified: 2025-10-19 20:28:35 | SHA1: 7988b4c569e4 + +#### Brief +name: Frontend CI + +#### Auto Summary +name: Frontend CI + +#### Content + +```yaml +name: Frontend CI + +on: + push: + pull_request: + +concurrency: + group: frontend-ci-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + test: + name: Jest on Node ${{ matrix.node }} + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + node: [18, 20] # LTS + current used in repo + + env: + CI: "1" + EXPO_TUNNEL: "false" + EXPO_DEVTOOLS_LISTEN_ADDRESS: 0.0.0.0 + EXPO_PUBLIC_API_BASE: http://localhost:8000 + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Use Node ${{ matrix.node }} + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node }} + cache: npm + cache-dependency-path: frontend/app/package-lock.json + + - name: Install deps + working-directory: frontend/app + run: npm ci + + - name: Type check (skip if no TS) + if: hashFiles('frontend/app/tsconfig.json') != '' + working-directory: frontend/app + run: | + npx --yes typescript@latest -v >/dev/null 2>&1 || true + npx tsc --noEmit || (echo "::warning::Type check failed"; exit 1) + + - name: Run Jest with coverage (CI mode) + working-directory: frontend/app + run: npm test -- --ci --runInBand --coverage --verbose + + - name: Upload coverage + if: always() + uses: actions/upload-artifact@v4 + with: + name: coverage-node${{ matrix.node }} + path: frontend/app/coverage + if-no-files-found: warn + + web-export-smoke: + name: Expo web export (smoke) + needs: test + runs-on: ubuntu-latest + env: + CI: "1" + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Use Node 20 + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + cache-dependency-path: frontend/app/package-lock.json + + - name: Install deps + working-directory: frontend/app + run: npm ci + + - name: Build (expo export -p web) + working-directory: frontend/app + run: npx expo export -p web + + - name: Upload dist (preview artifact) + if: always() + uses: actions/upload-artifact@v4 + with: + name: expo-web-dist + path: frontend/app/dist + if-no-files-found: error +``` + + +### 8. `.gitignore` +- Size: 585 bytes | LOC: 58 | SLOC: 49 | TODOs: 0 | Modified: 2025-10-19 20:28:35 | SHA1: 7b43f8e8f583 + +#### Brief +# Python +__pycache__/ + +#### Auto Summary +# Python + +#### Content + +``` +# Python +__pycache__/ +*.py[cod] +*.pyo +*.pyd +*.so +*.egg +*.egg-info/ +.eggs/ +*.log +*.sqlite3 +db.sqlite3 + +# Virtual env +.venv/ +env/ +venv/ +ENV/ + +# Jupyter +.ipynb_checkpoints/ + +# Node / React Native (Expo) +node_modules/ +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.expo/ +.expo-shared/ +web-build/ +dist/ +build/ + +# macOS / Linux / Windows +.DS_Store +Thumbs.db +desktop.ini + +# IDE +.vscode/ +.idea/ +*.swp + +# Docker +*.pid +*.sock +*.tar + +# coverage reports (backend & frontend) +coverage/ +backend/app/coverage/ +frontend/app/coverage/ + +# Jest cache +frontend/app/.jest-cache/ + +make_md.py +python_front_* +``` + + +### 9. `_config.yml` +- Size: 217 bytes | LOC: 10 | SLOC: 10 | TODOs: 0 | Modified: 2025-10-19 20:28:35 | SHA1: f87ea6343cf3 + +#### Brief +title: "python_front" +description: "A playground for Python" + +#### Auto Summary +title: "python_front" + +#### Content + +```yaml +title: "python_front" +description: "A playground for Python" +baseurl: "/python_front" +url: "https://europanite.github.io" +theme: minima +markdown: kramdown +plugins: + - jekyll-feed + - jekyll-sitemap +highlighter: rouge +``` + + +### 10. `CODE_OF_CONDUCT.md` +- Size: 4085 bytes | LOC: 68 | SLOC: 50 | TODOs: 0 | Modified: 2025-10-19 20:28:35 | SHA1: 724cc33f0dca + +#### Brief +# Contributor Covenant Code of Conduct + +#### Auto Summary +Contributor Covenant Code of Conduct + +#### Content (verbatim) + +```markdown +# Contributor Covenant Code of Conduct + +## Our Pledge +We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community. + +## Our Standards +Examples of behavior that contributes to a positive environment include: +- Demonstrating empathy and kindness toward other people +- Being respectful of differing opinions, viewpoints, and experiences +- Giving and gracefully accepting constructive feedback +- Taking responsibility and apologizing to those affected by our mistakes +- Focusing on what is best for the community, not just for ourselves + +Examples of unacceptable behavior include: +- The use of sexualized language or imagery, and sexual attention or advances +- Trolling, insulting or derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others’ private information without explicit permission +- Other conduct which could reasonably be considered inappropriate in a professional setting + +## Enforcement Responsibilities +Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +## Scope +This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. + +## Enforcement +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the maintainers of this project. All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the reporter of any incident. + +## Enforcement Guidelines +Community leaders will follow these guidelines in determining the consequences for any action they deem in violation of this Code of Conduct: + +1. **Correction** +- *Impact*: Use of inappropriate language or other behavior deemed unprofessional. +- *Consequence*: A private, written warning, with clarity around the nature of the violation and an explanation of why the behavior was inappropriate. + +2. **Warning** +- *Impact*: A violation through a single incident or series of actions. +- *Consequence*: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. + +3. **Temporary Ban** +- *Impact*: A serious violation of community standards, including sustained inappropriate behavior. +- *Consequence*: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. + +4. **Permanent Ban** +- *Impact*: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals. +- *Consequence*: A permanent ban from any sort of public interaction within the + community. + +## Attribution + +This Code of Conduct is adapted from the +- [Contributor Covenant][v2.1], version 2.1, available at [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. + +Community Impact Guidelines were inspired by +- [Mozilla’s code of conduct enforcement ladder][mozilla-coc]. + +For answers to common questions about this code of conduct, see the FAQ at +- [Contributor Covenant FAQ][faq]. + +[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html +[mozilla-coc]: https://github.com/mozilla/diversity +[faq]: https://www.contributor-covenant.org/faq +``` + + +### 11. `CONTRIBUTING.md` +- Size: 834 bytes | LOC: 25 | SLOC: 19 | TODOs: 0 | Modified: 2025-10-19 20:28:35 | SHA1: 12ea21d94090 + +#### Brief +# Contributing Guidelines + +#### Auto Summary +Contributing Guidelines + +#### Content (verbatim) + +```markdown +# Contributing Guidelines + +Thank you for considering contributing to this project! +We welcome bug reports, feature requests, and pull requests. +Please follow the guidelines below to make the process smooth for everyone. + +--- + +## How to Contribute + +### 1. Reporting Issues +- Check the [issue tracker](../../issues) to avoid duplicates. +- Use the appropriate [issue template](.github/ISSUE_TEMPLATE/) when creating a new issue. +- Provide as much detail as possible (steps to reproduce, expected behavior, environment, etc.). + +### 2. Suggesting Features +- Open a new **Feature Request** issue. +- Explain the problem your idea solves. +- Provide examples, use cases, or references if possible. + +### 3. Submitting Pull Requests +Fork the repository and create a new branch: + ```bash + git checkout -b feature/your-feature-name + ``` +``` + + +### 12. `docker-compose.test.yml` +- Size: 706 bytes | LOC: 21 | SLOC: 21 | TODOs: 0 | Modified: 2025-10-19 20:28:35 | SHA1: ae51e77ed333 + +#### Brief +services: + frontend_test: + +#### Auto Summary +services: + +#### Content + +```yaml +services: + frontend_test: + container_name: frontend_test + build: + context: ./frontend + dockerfile: Dockerfile.test + working_dir: /app + environment: + CI: "1" + EXPO_TUNNEL: ${EXPO_TUNNEL} + EXPO_DEVTOOLS_LISTEN_ADDRESS: ${EXPO_DEVTOOLS_LISTEN_ADDRESS} + REACT_NATIVE_PACKAGER_HOSTNAME: ${REACT_NATIVE_PACKAGER_HOSTNAME} + EXPO_PUBLIC_API_HOST: ${EXPO_PUBLIC_API_HOST} + EXPO_PUBLIC_API_PORT: ${EXPO_PUBLIC_API_PORT} + EXPO_PUBLIC_API_BASE: ${EXPO_PUBLIC_API_BASE} + volumes: + - ./frontend/app:/app + - /app/node_modules + - ./reports/frontend:/reports/frontend + command: >- + sh -lc "npm ci && npm test -- --ci --runInBand --verbose" +``` + + +### 13. `docker-compose.yml` +- Size: 887 bytes | LOC: 30 | SLOC: 30 | TODOs: 0 | Modified: 2025-10-19 20:28:35 | SHA1: 6798f1d1af83 + +#### Brief +services: + frontend: + +#### Auto Summary +services: + +#### Content + +```yaml +services: + frontend: + build: + context: ./frontend + dockerfile: Dockerfile + container_name: frontend + working_dir: /app + stdin_open: true + tty: true + restart: unless-stopped + volumes: + - ./frontend/app:/app + - /app/node_modules + environment: + EXPO_TUNNEL: ${EXPO_TUNNEL} + EXPO_DEVTOOLS_LISTEN_ADDRESS: ${EXPO_DEVTOOLS_LISTEN_ADDRESS} + REACT_NATIVE_PACKAGER_HOSTNAME: ${REACT_NATIVE_PACKAGER_HOSTNAME} + EXPO_PUBLIC_API_HOST: ${EXPO_PUBLIC_API_HOST} + EXPO_PUBLIC_API_PORT: ${EXPO_PUBLIC_API_PORT} + EXPO_PUBLIC_API_BASE: ${EXPO_PUBLIC_API_BASE} + ports: + - "19000:19000" # Expo dev server (Metro) + - "19001:19001" # React Native debugger(old) + - "19002:19002" # Expo web UI + - "8081:8081" # Metro bundler + command: > + sh -c " + npm install && + npx expo start + " +``` + + +### 14. `frontend/app/.gitignore` +- Size: 398 bytes | LOC: 37 | SLOC: 29 | TODOs: 0 | Modified: 2025-10-19 20:28:35 | SHA1: 87b612309282 + +#### Brief +# Learn more https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files + +#### Auto Summary +# Learn more https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files + +#### Content + +``` +# Learn more https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files + +# dependencies +node_modules/ + +# Expo +.expo/ +dist/ +web-build/ +expo-env.d.ts + +# Native +.kotlin/ +*.orig.* +*.jks +*.p8 +*.p12 +*.key +*.mobileprovision + +# Metro +.metro-health-check* + +# debug +npm-debug.* +yarn-debug.* +yarn-error.* + +# macOS +.DS_Store +*.pem + +# local env files +.env*.local + +# typescript +*.tsbuildinfo +``` + + +### 15. `frontend/app/__mocks__/expoConstantsMock.js` +- Size: 97 bytes | LOC: 3 | SLOC: 3 | TODOs: 0 | Modified: 2025-10-19 20:28:35 | SHA1: 042b2b318b7a + +#### Brief +module.exports = { + default: { expoConfig: {}, manifest: null, appOwnership: 'standalone' }, + +#### Auto Summary +module.exports = { + +#### Content + +```javascript +module.exports = { + default: { expoConfig: {}, manifest: null, appOwnership: 'standalone' }, +}; +``` + + +### 16. `frontend/app/__mocks__/expoMock.js` +- Size: 174 bytes | LOC: 7 | SLOC: 7 | TODOs: 0 | Modified: 2025-10-19 20:28:35 | SHA1: d58b65ebbe06 + +#### Brief +const dummy = new Proxy({}, { get: () => undefined }); +module.exports = { + +#### Auto Summary +const dummy = new Proxy({}, { get: () => undefined }); + +#### Content + +```javascript +const dummy = new Proxy({}, { get: () => undefined }); +module.exports = { + ...dummy, + registerRootComponent: () => {}, + installExpoGlobals: () => {}, + default: dummy, +}; +``` + + +### 17. `frontend/app/__mocks__/expoRouterMock.js` +- Size: 179 bytes | LOC: 7 | SLOC: 7 | TODOs: 0 | Modified: 2025-10-19 20:28:35 | SHA1: bec4abda4523 + +#### Brief +module.exports = { + useRouter: () => ({ push: () => {}, replace: () => {}, back: () => {} }), + +#### Auto Summary +module.exports = { + +#### Content + +```javascript +module.exports = { + useRouter: () => ({ push: () => {}, replace: () => {}, back: () => {} }), + useSegments: () => [], + Slot: () => null, + Stack: () => null, + default: {}, +}; +``` + + +### 18. `frontend/app/__tests__/auth.context.test.tsx` +- Size: 879 bytes | LOC: 32 | SLOC: 27 | TODOs: 0 | Modified: 2025-10-19 20:28:35 | SHA1: 8b991e2bc175 + +#### Brief +import React, { useEffect } from 'react'; +import { Text } from 'react-native'; + +#### Auto Summary +import React, { useEffect } from 'react'; + +#### Content + +```tsx +import React, { useEffect } from 'react'; +import { Text } from 'react-native'; +import { render, screen, waitFor, act } from '@testing-library/react-native'; +import { AuthProvider, useAuth } from '../context/Auth'; + +const fetchMock = jest.fn(); +global.fetch = fetchMock as any; + +function ShowUser() { + const { user } = useAuth(); + return {user ? user.email : 'none'}; +} + +let exposed: ReturnType | null = null; +function Expose() { + exposed = useAuth(); + return null; +} + +beforeEach(() => { + fetchMock.mockReset(); + exposed = null; +}); + +test('AuthProvider provides default null user', () => { + function ShowUser() { + const { user } = useAuth(); + return {user ? 'yes' : 'no'}; + } + render(); + expect(screen.getByTestId('user').props.children).toBe('no'); +}); +``` + + +### 19. `frontend/app/__tests__/home.screen.test.tsx` +- Size: 1421 bytes | LOC: 37 | SLOC: 24 | TODOs: 0 | Modified: 2025-10-19 20:28:35 | SHA1: da2189feb3a1 + +#### Brief +import React from 'react'; +import { render, screen } from '@testing-library/react'; + +#### Auto Summary +import React from 'react'; + +#### Content + +```tsx +import React from 'react'; +import { render, screen } from '@testing-library/react'; + +// Mock pyodide to avoid network and heavy init +jest.mock('pyodide', () => ({ + loadPyodide: jest.fn().mockResolvedValue({ + setStdout: jest.fn(), + setStderr: jest.fn(), + runPythonAsync: jest.fn().mockResolvedValue(undefined), + }), +})); + +// Mock clipboard used by Copy Output button +Object.assign(navigator, { clipboard: { writeText: jest.fn().mockResolvedValue(undefined) } }); + +// Mock react-navigation (only what HomeScreen touches indirectly) +jest.mock('@react-navigation/native', () => { + const actual = jest.requireActual('@react-navigation/native'); + return { ...actual, useNavigation: () => ({ navigate: jest.fn() }) }; +}); + +// Mock useAuth to a simple anonymous state +jest.mock('../context/Auth', () => ({ useAuth: jest.fn(() => ({ user: null })) })); + +const HomeScreen = require('../screens/HomeScreen').default; + +test('renders title, code and console areas', async () => { + render(); + // Title link + expect(await screen.findByText('Python Front')).toBeTruthy(); + // Buttons (labels come from MUI Buttons) + expect(screen.getByRole('button', { name: /Run/i })).toBeTruthy(); + expect(screen.getByRole('button', { name: /Clear/i })).toBeTruthy(); + expect(screen.getByRole('button', { name: /Load Sample/i })).toBeTruthy(); + // Console label + expect(screen.getByText('Console')).toBeTruthy(); +}); +``` + + +### 20. `frontend/app/__tests__/settingsbar.test.tsx` +- Size: 892 bytes | LOC: 23 | SLOC: 16 | TODOs: 0 | Modified: 2025-10-19 20:28:35 | SHA1: 1bac9d470083 + +#### Brief +import React from 'react'; +import { render, screen } from '@testing-library/react-native'; + +#### Auto Summary +import React from 'react'; + +#### Content + +```tsx +import React from 'react'; +import { render, screen } from '@testing-library/react-native'; + +// Mock useAuth +jest.mock('../context/Auth', () => ({ useAuth: jest.fn() })); +const { useAuth } = require('../context/Auth') as { useAuth: jest.Mock }; + +// Mock useNavigation +const mockNavigate = jest.fn(); +jest.mock('@react-navigation/native', () => { + const actual = jest.requireActual('@react-navigation/native'); + return { ...actual, useNavigation: () => ({ navigate: mockNavigate }) }; +}); + +beforeEach(() => { useAuth.mockReset(); mockNavigate.mockReset(); }); + +test('SettingsBar renders without crashing', () => { + const SettingsBar = require('../components/SettingsBar').default; + useAuth.mockReturnValue({ user: null, token: null, authHeader: () => ({}) }); + render(); + // No strict text to assert (bar is mostly layout), just verify render + expect(true).toBe(true); +}); +``` + + +### 21. `frontend/app/__tests__/smoke.test.tsx` +- Size: 288 bytes | LOC: 10 | SLOC: 8 | TODOs: 0 | Modified: 2025-10-19 20:28:35 | SHA1: 29a77aa1510f + +#### Brief +import React from 'react'; +import { Text } from 'react-native'; + +#### Auto Summary +import React from 'react'; + +#### Content + +```tsx +import React from 'react'; +import { Text } from 'react-native'; +import { render, screen } from '@testing-library/react-native'; + +function Hello(){ return hi; } + +test('smoke', async () => { + render(); + expect(screen.getByTestId('hi')).toBeTruthy(); +}); +``` + + +### 22. `frontend/app/app.json` +- Size: 745 bytes | LOC: 34 | SLOC: 34 | TODOs: 0 | Modified: 2025-10-19 20:28:35 | SHA1: 348660964aeb + +#### Brief +{ + "expo": { + +#### Auto Summary +{ + +#### Content + +```json +{ + "expo": { + "name": "app", + "slug": "app", + "version": "1.0.0", + "orientation": "portrait", + "icon": "./assets/icon.png", + "userInterfaceStyle": "light", + "newArchEnabled": true, + "splash": { + "image": "./assets/splash-icon.png", + "resizeMode": "contain", + "backgroundColor": "#ffffff" + }, + "ios": { + "supportsTablet": true + }, + "android": { + "adaptiveIcon": { + "foregroundImage": "./assets/adaptive-icon.png", + "backgroundColor": "#ffffff" + }, + "edgeToEdgeEnabled": true + }, + "experiments": { + "baseUrl": "/python_front" + }, + "web": { + "favicon": "./assets/favicon.png", + "output": "single", + "bundler": "metro" + } + } +} +``` + + +### 23. `frontend/app/App.tsx` +- Size: 991 bytes | LOC: 30 | SLOC: 27 | TODOs: 0 | Modified: 2025-10-19 20:28:35 | SHA1: c327cd2f2101 + +#### Brief +import React from "react"; +import { View } from "react-native"; + +#### Auto Summary +import React from "react"; + +#### Content + +```tsx +import React from "react"; +import { View } from "react-native"; +import { NavigationContainer } from "@react-navigation/native"; +import { createNativeStackNavigator } from "@react-navigation/native-stack"; +import { SafeAreaProvider } from "react-native-safe-area-context"; +import { StatusBar } from "expo-status-bar"; + +import { AuthProvider } from "./context/Auth"; +import SettingsBar from "./components/SettingsBar"; +import HomeScreen from "./screens/HomeScreen"; + +const Stack = createNativeStackNavigator(); + +export default function App() { + return ( + + + + + + + + + + + + + + ); +} +``` + + +### 24. `frontend/app/babel.config.js` +- Size: 294 bytes | LOC: 15 | SLOC: 15 | TODOs: 0 | Modified: 2025-10-19 20:28:35 | SHA1: d5c5f238e459 + +#### Brief +module.exports = function (api) { + api.cache(true); + +#### Auto Summary +module.exports = function (api) { + +#### Content + +```javascript +module.exports = function (api) { + api.cache(true); + return { + presets: ['babel-preset-expo'], + env: { + test: { + plugins: [ + ['transform-inline-environment-variables', { + include: ['EXPO_PUBLIC_API_BASE'] + }], + ], + }, + }, + }; +}; +``` + + +### 25. `frontend/app/components/SettingsBar.tsx` +- Size: 1656 bytes | LOC: 51 | SLOC: 47 | TODOs: 0 | Modified: 2025-10-19 20:28:35 | SHA1: 923ce657afe8 + +#### Brief +import React from "react"; +import { View, Text, TouchableOpacity, useWindowDimensions } from "react-native"; + +#### Auto Summary +import React from "react"; + +#### Content + +```tsx +import React from "react"; +import { View, Text, TouchableOpacity, useWindowDimensions } from "react-native"; +import { SafeAreaView } from "react-native-safe-area-context"; +import { StatusBar } from "expo-status-bar"; +import { useAuth } from "../context/Auth"; +import { useNavigation } from "@react-navigation/native"; + +const BAR_BG = "#000000ff"; +const CONTENT_MAX_W = 480; // ← same as forms + +export default function SettingsBar() { + const nav = useNavigation(); + const { width } = useWindowDimensions(); + const isNarrow = width < 420; // stack buttons below on very small widths + + const Btn = ({ title, onPress }: { title: string; onPress: () => void }) => ( + + {title} + + ); + + return ( + + + + + + + + + + + + ); +} +``` + + +### 26. `frontend/app/context/Auth.tsx` +- Size: 1001 bytes | LOC: 35 | SLOC: 25 | TODOs: 0 | Modified: 2025-10-19 20:28:35 | SHA1: 8a2139845413 + +#### Brief +Simple auth context that stores token/email in memory. +You can later persist it with AsyncStorage if needed. + +#### Auto Summary +// Simple auth context that stores token/email in memory. + +#### Content + +```tsx +// Simple auth context that stores token/email in memory. +// You can later persist it with AsyncStorage if needed. +import React, { createContext, useContext, useState, ReactNode } from "react"; + +type User = { email: string } | null; + +type AuthCtx = { + user: User; + token: string | null; + authHeader: () => Partial>; +}; + +const AuthContext = createContext(null); + +const API_BASE = process.env.EXPO_PUBLIC_API_BASE!; + +export function AuthProvider({ children }: { children: ReactNode }) { + const [token, setToken] = useState(null); + const [user, setUser] = useState(null); + + const authHeader = () => + token ? { Authorization: `Bearer ${token}` } : {}; + + return ( + + {children} + + ); +} + +export function useAuth() { + const ctx = useContext(AuthContext); + if (!ctx) throw new Error("useAuth must be used within AuthProvider"); + return ctx; +} +``` + + +### 27. `frontend/app/index.ts` +- Size: 307 bytes | LOC: 8 | SLOC: 3 | TODOs: 0 | Modified: 2025-10-19 20:28:35 | SHA1: fd1e69b3b668 + +#### Brief +import { registerRootComponent } from 'expo'; + +#### Auto Summary +import { registerRootComponent } from 'expo'; + +#### Content + +```typescript +import { registerRootComponent } from 'expo'; + +import App from './App'; + +// registerRootComponent calls AppRegistry.registerComponent('main', () => App); +// It also ensures that whether you load the app in Expo Go or in a native build, +// the environment is set up appropriately +registerRootComponent(App); +``` + + +### 28. `frontend/app/jest.config.ts` +- Size: 1011 bytes | LOC: 29 | SLOC: 29 | TODOs: 0 | Modified: 2025-10-19 20:28:35 | SHA1: 6aa9c9ed7996 + +#### Brief +module.exports = { + preset: 'jest-expo', + +#### Auto Summary +module.exports = { + +#### Content + +```typescript + module.exports = { + preset: 'jest-expo', + testEnvironment: 'jsdom', + setupFilesAfterEnv: ['/jest.setup.ts'], + moduleNameMapper: { + '\\.(png|jpe?g|gif|svg)$': '/__mocks__/fileMock.js', + '\\.(css|scss)$': '/__mocks__/styleMock.js', + 'react-native-reanimated': 'react-native-reanimated/mock', + '^expo($|/.+)': '/__mocks__/expoMock.js', + '^expo-router($|/.+)': '/__mocks__/expoRouterMock.js', + '^expo-constants$': '/__mocks__/expoConstantsMock.js', + }, + setupFiles: ['/jest.setupFiles.js'], + transformIgnorePatterns: [ + 'node_modules/(?!(react-native' + + '|@react-native' + + '|react-clone-referenced-element' + + '|@react-navigation' + + '|react-navigation' + + '|expo(nent)?' + + '|@expo(nent)?/.*' + + '|expo-modules-core' + + '|expo-.*' + + '|@expo/.*' + + '|@react-native/polyfills' + + ')/)', + ], + testMatch: ['**/__tests__/**/*.test.(ts|tsx)'], + }; +``` + + +### 29. `frontend/app/jest.setup.ts` +- Size: 88 bytes | LOC: 2 | SLOC: 2 | TODOs: 0 | Modified: 2025-10-19 20:28:35 | SHA1: f764402eb556 + +#### Brief +import '@testing-library/jest-native/extend-expect'; +import '@testing-library/jest-dom'; + +#### Auto Summary +import '@testing-library/jest-native/extend-expect'; + +#### Content + +```typescript +import '@testing-library/jest-native/extend-expect'; +import '@testing-library/jest-dom'; +``` + + +### 30. `frontend/app/jest.setupFiles.js` +- Size: 395 bytes | LOC: 9 | SLOC: 8 | TODOs: 0 | Modified: 2025-10-19 20:28:35 | SHA1: 8572a798e340 + +#### Brief +if (!Object.getOwnPropertyDescriptor(globalThis, '__ExpoImportMetaRegistry')) { + Object.defineProperty(globalThis, '__ExpoImportMetaRegistry', { + +#### Auto Summary +if (!Object.getOwnPropertyDescriptor(globalThis, '__ExpoImportMetaRegistry')) { + +#### Content + +```javascript +if (!Object.getOwnPropertyDescriptor(globalThis, '__ExpoImportMetaRegistry')) { + Object.defineProperty(globalThis, '__ExpoImportMetaRegistry', { + configurable: true, + value: { get: () => null, has: () => false }, + }); +} + +jest.mock('@react-native/animated', () => ({}), { virtual: true }); +jest.mock('react-native/Libraries/Animated/NativeAnimatedHelper', () => ({}), { virtual: true }); +``` + + +### 31. `frontend/app/metro.config.js` +- Size: 128 bytes | LOC: 3 | SLOC: 3 | TODOs: 0 | Modified: 2025-10-19 20:28:35 | SHA1: 2c699d1e4a93 + +#### Brief +const { getDefaultConfig } = require('expo/metro-config'); +const config = getDefaultConfig(__dirname); + +#### Auto Summary +const { getDefaultConfig } = require('expo/metro-config'); + +#### Content + +```javascript +const { getDefaultConfig } = require('expo/metro-config'); +const config = getDefaultConfig(__dirname); +module.exports = config; +``` + + +### 32. `frontend/app/public/404.html` +- Size: 464 bytes | LOC: 12 | SLOC: 12 | TODOs: 0 | Modified: 2025-10-19 20:28:35 | SHA1: c9595010e927 + +#### Brief + + Redirecting... + +#### Auto Summary + + +#### Content + +```html + + Redirecting... + +Redirecting... +``` + + +### 33. `frontend/app/public/google095bf08db4fb15d0.html` +- Size: 53 bytes | LOC: 1 | SLOC: 1 | TODOs: 0 | Modified: 2025-10-19 20:28:35 | SHA1: 6d7f2ccca405 + +#### Brief +google-site-verification: google095bf08db4fb15d0.html + +#### Auto Summary +google-site-verification: google095bf08db4fb15d0.html + +#### Content + +```html +google-site-verification: google095bf08db4fb15d0.html +``` + + +### 34. `frontend/app/screens/HomeScreen.tsx` +- Size: 8293 bytes | LOC: 271 | SLOC: 242 | TODOs: 0 | Modified: 2025-10-19 20:28:35 | SHA1: e7811d526f20 + +#### Brief +import React, { useEffect, useMemo, useRef, useState } from "react"; +import { useWindowDimensions } from "react-native"; + +#### Auto Summary +import React, { useEffect, useMemo, useRef, useState } from "react"; + +#### Content + +```tsx +import React, { useEffect, useMemo, useRef, useState } from "react"; +import { useWindowDimensions } from "react-native"; +import Button from "@mui/material/Button"; +import Textarea from "@mui/joy/Textarea"; +import CircularProgress from "@mui/material/CircularProgress"; +import Stack from "@mui/material/Stack"; +import Chip from "@mui/material/Chip"; +import Divider from "@mui/material/Divider"; +import Paper from "@mui/material/Paper"; +import Tooltip from "@mui/material/Tooltip"; +import { loadPyodide, PyodideInterface } from "pyodide"; + +/** + * HomeScreen (Vertical layout, toolbar on top) + * --------------------------------------------- + * [ Top Toolbar: Title + Buttons + Status ] + * [ Code Window (flex: 1) ] + * [ Console Window (short, fixed height) ] + */ + +type ExecResult = + | { isError?: false; text: string } + | { isError: true; text: string }; + +const DEFAULT_SAMPLE = `# Sample: +# print number +a = 1 +b = 2 +c = a + b +print(c) +`; + +export default function HomeScreen() { + const [pyodide, setPyodide] = useState(null); + const [status, setStatus] = useState<"idle" | "loading" | "ready" | "running">("loading"); + const [code, setCode] = useState(DEFAULT_SAMPLE); + const [consoleText, setConsoleText] = useState(""); + const consoleRef = useRef(null); + + // soft-cancel token for "Stop" + const execIdRef = useRef(0); + + const { height, width } = useWindowDimensions(); + // Keep console clearly smaller than before (≈ 20% of viewport, clamped) + const consoleHeight = Math.max(120, Math.min(200, Math.round(height * 0.22))); + const narrow = width < 720; + + const styles = useMemo(() => { + return { + root: { + width: "100%", + maxWidth: 1100, + margin: "0 auto", + padding: 12, + display: "flex", + flexDirection: "column" as const, + gap: 12, + minHeight: height - 60, + }, + toolbar: { + display: "flex", + alignItems: "center", + justifyContent: "space-between", + gap: 12, + flexWrap: "wrap" as const, + }, + title: { fontSize: 22, fontWeight: 800, margin: 0 }, + codePaper: { + flex: 1, + display: "flex", + flexDirection: "column" as const, + gap: 8, + padding: 12, + minHeight: 260, + }, + codeTextarea: { + fontFamily: "ui-monospace, Menlo, Monaco, Consolas, monospace", + fontSize: 14, + minHeight: 220, + flex: 1, + }, + consolePaper: { + padding: 12, + display: "flex", + flexDirection: "column" as const, + gap: 8, + }, + consoleBox: { + height: consoleHeight, + background: "#0b0b0b", + color: "#e6e6e6", + borderRadius: 8, + padding: 12, + overflow: "auto" as const, + fontFamily: "ui-monospace, Menlo, Monaco, Consolas, monospace", + fontSize: 14, + border: "1px solid #333", + lineHeight: 1.35, + whiteSpace: "pre-wrap" as const, + }, + }; + }, [height, width, consoleHeight]); + + // auto-scroll console + useEffect(() => { + if (consoleRef.current) consoleRef.current.scrollTop = consoleRef.current.scrollHeight; + }, [consoleText]); + + // load Pyodide once + useEffect(() => { + (async () => { + try { + setStatus("loading"); + const instance = await loadPyodide({ + indexURL: "https://cdn.jsdelivr.net/pyodide/v0.23.0/full/", + }); + setPyodide(instance); + setStatus("ready"); + } catch (e) { + appendConsole({ isError: true, text: toErrMsg(e) }); + setStatus("idle"); + } + })(); + }, []); + + function toErrMsg(err: any) { + return err instanceof Error ? err.message : String(err); + } + function appendConsole(res: ExecResult) { + setConsoleText((prev) => { + const line = res.isError ? `[ERROR] ${res.text}` : res.text; + return prev ? `${prev}\n${line}` : line; + }); + } + function clearConsole() { + setConsoleText(""); + } + function loadSample() { + setCode(DEFAULT_SAMPLE); + } + async function copyOutput() { + try { + await navigator.clipboard.writeText(consoleText); + appendConsole({ text: "[INFO] Output copied to clipboard." }); + } catch { + appendConsole({ isError: true, text: "Failed to copy output." }); + } + } + + async function run() { + if (!pyodide || status !== "ready") return; + setStatus("running"); + const myExecId = ++execIdRef.current; + + try { + // capture streams + const lines: string[] = []; + pyodide.setStdout({ batched: (t: string) => lines.push(t) }); + pyodide.setStderr({ batched: (t: string) => lines.push(t) }); + + // soft timeout guard + const TIMEOUT_MS = 30_000; + const p = pyodide.runPythonAsync(code); + await Promise.race([p, new Promise((_, rej) => setTimeout(() => rej(new Error("Execution timeout")), TIMEOUT_MS))]); + + // if not stopped mid-run, flush + if (myExecId === execIdRef.current) { + const out = lines.join(""); + appendConsole({ text: out || "(no output)" }); + } + } catch (e) { + if (myExecId === execIdRef.current) appendConsole({ isError: true, text: toErrMsg(e) }); + } finally { + if (myExecId === execIdRef.current) setStatus("ready"); + } + } + + function stop() { + // Soft-cancel: bump token so any in-flight run discards its result + execIdRef.current++; + appendConsole({ isError: true, text: "Stopped current execution." }); + // Note: For true hard-cancel, move execution to a WebWorker and terminate it. + } + + const statusChip = ( + : undefined} + sx={{ fontWeight: 700 }} + /> + ); + + return ( +
+ {/* Top toolbar: Title + Buttons + Status */} +
+

+ + Python Front + +

+ + + + + + + + + + + + + {statusChip} +
+ + {/* Code window (big, flexible) */} + + Code + +