pyweb-stack 0.6.1__tar.gz → 0.6.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/CHANGELOG.md +59 -0
- {pyweb_stack-0.6.1/pyweb_stack.egg-info → pyweb_stack-0.6.2}/PKG-INFO +15 -4
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/README.md +14 -3
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/THREAT_MODEL.md +15 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/docs/01-introduction.md +4 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/docs/02-quickstart.md +7 -1
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/docs/14-cli.md +14 -8
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/docs/16-limitations-roadmap.md +10 -6
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/docs/17-ai-assistants.md +20 -0
- pyweb_stack-0.6.2/docs/35-dev-loop.md +151 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyproject.toml +1 -1
- pyweb_stack-0.6.2/pyweb/_templates.py +18 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/app_loader.py +27 -2
- pyweb_stack-0.6.2/pyweb/assist.py +394 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/bench.py +33 -1
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/cli/__init__.py +282 -20
- pyweb_stack-0.6.2/pyweb/compiler/diskcache.py +72 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/compiler/lower.py +2 -2
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/compiler/parser.py +56 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/compiler/pipeline.py +16 -4
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/devlink.py +37 -3
- pyweb_stack-0.6.2/pyweb/devopen.py +92 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/hosting.py +4 -3
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/mcp.py +80 -0
- pyweb_stack-0.6.2/pyweb/qr.py +273 -0
- pyweb_stack-0.6.2/pyweb/runtime/browser/dev/assist.js +156 -0
- pyweb_stack-0.6.2/pyweb/runtime/browser/dev/inspect.js +138 -0
- pyweb_stack-0.6.2/pyweb/runtime/browser/dev/reload.js +180 -0
- pyweb_stack-0.6.2/pyweb/runtime/browser/dev/timeline.js +144 -0
- pyweb_stack-0.6.2/pyweb/runtime/browser/dev/welcome.js +46 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/runtime/browser/runtime.js +4 -3
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/runtime/server/__init__.py +2 -0
- pyweb_stack-0.6.2/pyweb/share.py +172 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/telemetry/devtools.py +4 -1
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2/pyweb_stack.egg-info}/PKG-INFO +15 -4
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb_stack.egg-info/SOURCES.txt +15 -0
- pyweb_stack-0.6.2/tests/test_assist.py +243 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_compile_cache.py +62 -1
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_dev_reload.py +5 -3
- pyweb_stack-0.6.2/tests/test_devopen.py +87 -0
- pyweb_stack-0.6.2/tests/test_first_run.py +119 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_mcp.py +1 -1
- pyweb_stack-0.6.2/tests/test_qr.py +103 -0
- pyweb_stack-0.6.2/tests/test_share.py +153 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_trusted_types.py +8 -0
- pyweb_stack-0.6.1/pyweb/_templates.py +0 -3
- pyweb_stack-0.6.1/pyweb/runtime/browser/dev/reload.js +0 -102
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/LICENSE +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/MANIFEST.in +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/docs/03-tutorial.md +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/docs/04-pyweb-files.md +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/docs/05-reactivity.md +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/docs/06-server-functions.md +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/docs/07-browser-python.md +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/docs/08-pages-routing-assets.md +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/docs/09-data.md +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/docs/10-auth.md +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/docs/11-testing.md +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/docs/12-deployment.md +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/docs/13-security.md +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/docs/15-toolkit.md +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/docs/18-npm-packages.md +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/docs/19-layouts-navigation.md +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/docs/20-ai-apps.md +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/docs/21-live-data.md +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/docs/22-recipe-web3.md +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/docs/23-forms.md +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/docs/24-jobs.md +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/docs/25-scaling.md +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/docs/26-observability.md +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/docs/27-upgrading.md +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/docs/28-saas-tutorial.md +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/docs/29-performance.md +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/docs/30-teams.md +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/docs/31-pwa-push.md +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/docs/32-desktop.md +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/docs/33-webhooks-files.md +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/docs/34-ui-and-motion.md +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/__init__.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/_speedups.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/a11y.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/ai/guide.md +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/app.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/asgi.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/auth.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/authkit/__init__.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/authkit/admin.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/authkit/html.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/authkit/models.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/authkit/oauth.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/authkit/orgpages.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/authkit/orgs.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/authkit/pages.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/authkit/passwords.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/authkit/privacy.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/authkit/teams.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/authkit/throttle.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/authkit/tokens.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/authkit/webauthn.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/browser.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/build.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/cache.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/cli/__main__.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/compiler/__init__.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/compiler/ast.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/compiler/codegen/__init__.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/compiler/codegen/ir.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/compiler/errors.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/compiler/pyjs.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/compiler/rpc.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/config.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/context.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/csp_report.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/css.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/db/__init__.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/db/dialect.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/db/migrate.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/db/rls.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/db/schema.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/db/seeds.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/decorators.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/deploy/__init__.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/deploy/legacy.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/deploy/plan.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/deploy/targets.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/deprecation.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/desktop/__init__.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/desktop/gate.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/desktop/package.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/desktop/shell.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/doctor.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/dts.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/fieldcrypt.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/filegc.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/forms.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/http.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/jobs/__init__.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/jobs/builtin.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/jobs/core.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/jobs/db.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/jobs/legacy.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/jobs/memory.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/jobs/redis.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/jobs/schedule.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/jobs/worker.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/keys.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/livedata.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/livetable.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/lsp.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/lsp_data.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/lsp_teams.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/lsp_ui.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/mail.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/markdown.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/mcp_http.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/mcp_tools.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/models/__init__.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/models/fields.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/models/query.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/models/relations.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/motion.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/net/__init__.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/net/http.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/net/live.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/net/server.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/net/ws.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/observability.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/openapi.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/packages.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/platform.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/plugins.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/profile.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/push.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/pwa.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/py.typed +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/realtime.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/rooms.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/rpc.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/rules.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/runtime/browser/auth.js +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/runtime/browser/desktop.js +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/runtime/browser/forms.js +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/runtime/browser/live.js +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/runtime/browser/markdown.js +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/runtime/browser/motion-api.js +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/runtime/browser/motion-count.js +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/runtime/browser/motion-drag.js +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/runtime/browser/motion-layout.js +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/runtime/browser/motion-math.js +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/runtime/browser/motion.js +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/runtime/browser/pwa.js +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/runtime/browser/sw.js +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/runtime/browser/ui/accordion.js +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/runtime/browser/ui/calendar.js +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/runtime/browser/ui/carousel.js +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/runtime/browser/ui/checkbox.js +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/runtime/browser/ui/collapsible.js +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/runtime/browser/ui/combobox.js +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/runtime/browser/ui/command.js +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/runtime/browser/ui/context-menu.js +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/runtime/browser/ui/core.js +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/runtime/browser/ui/dialog.js +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/runtime/browser/ui/hover-card.js +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/runtime/browser/ui/index.js +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/runtime/browser/ui/listbox.js +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/runtime/browser/ui/menu.js +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/runtime/browser/ui/menubar.js +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/runtime/browser/ui/navigation-menu.js +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/runtime/browser/ui/otp.js +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/runtime/browser/ui/popover.js +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/runtime/browser/ui/radio-group.js +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/runtime/browser/ui/resizable.js +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/runtime/browser/ui/select.js +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/runtime/browser/ui/sidebar.js +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/runtime/browser/ui/slider.js +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/runtime/browser/ui/tabs.js +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/runtime/browser/ui/toaster.js +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/runtime/browser/ui/toggle-group.js +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/runtime/browser/ui/toggle.js +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/runtime/browser/ui/tooltip.js +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/runtime/server/argcheck.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/sbom.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/secret_scan.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/security.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/serve.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/ssr.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/ssr_codegen.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/storage.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/sync.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/telemetry/__init__.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/telemetry/instruments.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/telemetry/logs.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/telemetry/metrics.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/telemetry/tracing.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/templates/ai-chat.pyweb +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/templates/app.css +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/templates/auth.pyweb +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/templates/blog.pyweb +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/templates/chat.pyweb +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/templates/counter.pyweb +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/templates/desktop/app.pyweb +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/templates/desktop/test_app.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/templates/saas/app.pyweb +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/templates/saas/seeds.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/templates/saas/test_app.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/templates/todo.pyweb +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/tenancy.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/testing.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/ui/__init__.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/ui/cli.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/ui/extending.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/ui/kit/button.pyweb +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/ui/kit/card.pyweb +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/ui/kit/carousel.pyweb +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/ui/kit/chart.pyweb +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/ui/kit/command.pyweb +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/ui/kit/controls.pyweb +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/ui/kit/data.pyweb +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/ui/kit/date.pyweb +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/ui/kit/dialog.pyweb +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/ui/kit/disclosure.pyweb +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/ui/kit/display.pyweb +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/ui/kit/layout.pyweb +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/ui/kit/menu.pyweb +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/ui/kit/overlay.pyweb +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/ui/kit/style.pyweb +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/ui/serve.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/ui/source_edit.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/ui/static/ui.css +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/ui/theme.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/upgrade.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/uploads.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb/webhooks.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb_stack.egg-info/dependency_links.txt +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb_stack.egg-info/entry_points.txt +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb_stack.egg-info/requires.txt +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/pyweb_stack.egg-info/top_level.txt +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/setup.cfg +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/setup.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_account_privacy.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_api_tokens.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_auth.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_auth_contract.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_auth_security.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_auth_sessions.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_auth_v1.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_authkit.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_authkit_units.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_backplane.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_browser_build.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_budget.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_cli_deploy.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_cli_test.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_codegen.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_compiler.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_cron.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_customise.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_data_layer.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_db_production.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_deploy_upload_render.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_deploy_v5.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_desktop.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_docs.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_doctor.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_dts.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_dx_v5.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_e2e.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_encrypted_fields.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_error_messages.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_examples.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_filegc.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_forms_security_obs.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_forms_v5.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_fuzz_requests.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_http_fetch.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_jobs_app.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_jobs_cache_realtime.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_jobs_v5.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_keys_sessions.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_live_patches.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_livedata.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_lsp.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_lsp_teams.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_markdown.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_mcp_teams.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_mcp_v6.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_migrations_property.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_migrations_v5.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_models_db.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_motion.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_multifile.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_multipage.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_net.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_observability.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_openapi.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_org_pages.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_packages.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_parser.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_parser_v1.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_plugins_platform_bench.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_production_gaps.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_profile.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_push.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_pwa.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_pyjs_semantics.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_rate_per_function.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_reactivity.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_realtime_transport.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_reference_apps.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_request_hardening.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_rpc_placement.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_rpc_production.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_runtime_signals.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_sbom.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_secret_scan.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_server_db.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_speedups.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_ssr_codegen.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_static_hashing.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_static_serving.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_streaming.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_sync_live.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_systems.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_telemetry.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_tenancy.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_testing_orgs.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_threat_model.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_ui_kit.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_webhooks.py +0 -0
- {pyweb_stack-0.6.1 → pyweb_stack-0.6.2}/tests/test_website.py +0 -0
|
@@ -6,6 +6,65 @@ project uses [semantic versioning](https://semver.org).
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [0.6.2]
|
|
10
|
+
|
|
11
|
+
The dev loop. Saves update the open page in place with every page and
|
|
12
|
+
component keeping its state, any element opens its own line in your
|
|
13
|
+
editor, error pages fix themselves with AI (your assistant's model through
|
|
14
|
+
MCP, or your own key), a timeline steps back through every change, and
|
|
15
|
+
`pyweb dev --share` gives someone a link to try what you're building. All
|
|
16
|
+
of it lives in `pyweb dev`: production output is unchanged and there is
|
|
17
|
+
nothing to change in your apps. See [The dev loop](docs/35-dev-loop.md).
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
- **Hot reload without a page load.** A save swaps the page in place
|
|
21
|
+
through client navigation: no reload, no flash. Page variables,
|
|
22
|
+
component variables (each instance keeps its own), form fields, scroll,
|
|
23
|
+
focus and the selection stay. A stylesheet-only save swaps just that
|
|
24
|
+
stylesheet. When the page changed too much to swap, it reloads with the
|
|
25
|
+
same state, also after a compile error is fixed.
|
|
26
|
+
- **Click to source.** Hold Alt to see where any element comes from;
|
|
27
|
+
Alt-click opens that line in your editor (`PYWEB_EDITOR`, else VS Code,
|
|
28
|
+
Cursor, Windsurf, Zed or Sublime Text, else a `vscode://` link) and
|
|
29
|
+
shows the **inspector**: the components around it with their live
|
|
30
|
+
variables, which you can edit.
|
|
31
|
+
- **Fix with AI and Edit with AI.** Compile error pages and dev 500 pages
|
|
32
|
+
have Fix with AI; Alt+Shift-click any element to ask for a change. The
|
|
33
|
+
answer is a compiled diff of one app file: Apply writes it (refused if
|
|
34
|
+
the file changed since) and Undo puts it back (`.pyweb/history/`). It is
|
|
35
|
+
answered by the assistant connected through `pyweb mcp` when its app
|
|
36
|
+
supports MCP sampling, else `ANTHROPIC_API_KEY` or
|
|
37
|
+
`OPENAI_API_KEY`/`OPENAI_BASE_URL`, else the page offers the prompt to
|
|
38
|
+
copy. Credentials in what's sent are replaced by placeholders and put
|
|
39
|
+
back in the answer.
|
|
40
|
+
- **Time-travel timeline.** The toolbar's Timeline tab records every change
|
|
41
|
+
to the page's variables with its cause (a click, typing, a server call);
|
|
42
|
+
a slider steps back through them with the page paused, and Resume
|
|
43
|
+
returns to now.
|
|
44
|
+
- **`pyweb dev --share`**: a temporary public link (cloudflared, else
|
|
45
|
+
localhost.run) with a QR code. The link's token sets a cookie that every
|
|
46
|
+
request through the tunnel needs; guests get the app and hot reload, not
|
|
47
|
+
your tools.
|
|
48
|
+
- **A better first five minutes.** `pyweb new` with no name asks for the
|
|
49
|
+
name, a starter and a colour preset (`--theme` without asking); `pyweb
|
|
50
|
+
dev` opens the browser, prints the network URL with a QR code for a phone
|
|
51
|
+
with `--host 0.0.0.0`, and shows a one-time Welcome panel with what to
|
|
52
|
+
try and how to connect your assistant.
|
|
53
|
+
- **A faster first start.** Parse results and compiled code are cached on
|
|
54
|
+
disk (`~/.cache/pyweb`; `PYWEB_CACHE=0` turns it off): loading the saas
|
|
55
|
+
example in a fresh process went from about 340 to 210 ms.
|
|
56
|
+
`pyweb.bench --system` reports `cold_dev_ms`.
|
|
57
|
+
- MCP: the `pyweb_assist_requests` tool and the `fix_errors` prompt;
|
|
58
|
+
`pyweb mcp` answers the page's AI requests through sampling.
|
|
59
|
+
|
|
60
|
+
### Changed
|
|
61
|
+
- `pyweb dev`'s toolbar and its `/__pyweb/dev/` endpoints answer only the
|
|
62
|
+
person running it: a request from this machine to a loopback address.
|
|
63
|
+
Visitors from the network (`--host 0.0.0.0`) or a share link get hot
|
|
64
|
+
reload only.
|
|
65
|
+
- The runtime is 25 bytes bigger (gzip) for the dev hooks; nothing runs
|
|
66
|
+
in production.
|
|
67
|
+
|
|
9
68
|
## [0.6.1]
|
|
10
69
|
|
|
11
70
|
Better error messages, `pyweb doctor`, a `pyweb dev` that keeps your place
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pyweb-stack
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.2
|
|
4
4
|
Summary: Full-stack web apps in one Python file: server-rendered pages, reactive browser UI, typed RPC.
|
|
5
5
|
Author-email: MaanavKrishna <67054795+MaanavKrishna@users.noreply.github.com>
|
|
6
6
|
Maintainer-email: MaanavKrishna <67054795+MaanavKrishna@users.noreply.github.com>
|
|
@@ -400,6 +400,17 @@ develop a toolbar on every page shows each request's queries and flags
|
|
|
400
400
|
N+1 loops. [Scaling & deploying →](docs/25-scaling.md) ·
|
|
401
401
|
[Logs, metrics & tracing →](docs/26-observability.md)
|
|
402
402
|
|
|
403
|
+
### A dev loop that keeps up
|
|
404
|
+
|
|
405
|
+
Save, and the open page changes in place: no reload, and every page and
|
|
406
|
+
component keeps its state. Hold Alt and click any element to open its
|
|
407
|
+
line in your editor; Alt+Shift-click to change it with AI, and error
|
|
408
|
+
pages have **Fix with AI** (your assistant's model through MCP, or your
|
|
409
|
+
own API key), with a diff to apply and Undo. The toolbar's timeline
|
|
410
|
+
steps back through every change and what caused it, and
|
|
411
|
+
`pyweb dev --share` gives someone a link to try what you're building.
|
|
412
|
+
[The dev loop →](docs/35-dev-loop.md)
|
|
413
|
+
|
|
403
414
|
### Small, fast and boring to run
|
|
404
415
|
|
|
405
416
|
- **Every page is server-rendered**: complete HTML on first paint, then
|
|
@@ -442,7 +453,7 @@ it runs the real PyWeb, server functions included, in your browser.
|
|
|
442
453
|
pip install pyweb-stack
|
|
443
454
|
pyweb new myapp --template saas # saas | desktop | blank | counter | todo | blog | auth | chat | ai-chat
|
|
444
455
|
cd myapp
|
|
445
|
-
pyweb dev app.pyweb #
|
|
456
|
+
pyweb dev app.pyweb # saves update the open page in place, state kept
|
|
446
457
|
```
|
|
447
458
|
|
|
448
459
|
Then:
|
|
@@ -478,7 +489,7 @@ The [architecture overview](ARCHITECTURE.md) goes through each stage.
|
|
|
478
489
|
|
|
479
490
|
## Build it with AI
|
|
480
491
|
|
|
481
|
-
PyWeb ships an MCP server with
|
|
492
|
+
PyWeb ships an MCP server with 31 tools, so AI assistants can do what
|
|
482
493
|
you'd do: scaffold an app, search the docs, check each edit (errors come
|
|
483
494
|
back as line numbers with fix hints), build with the UI kit and a theme,
|
|
484
495
|
render pages and call server functions as any signed-in member, look at
|
|
@@ -566,7 +577,7 @@ Upgrade with `pip install -U pyweb-stack`.
|
|
|
566
577
|
|
|
567
578
|
| Version | Highlights |
|
|
568
579
|
|---|---|
|
|
569
|
-
| 0.6 | Teams with roles, invitations and tenant-scoped Models (Postgres row-level security), API tokens and OpenAPI, webhooks, installable apps with Web Push, desktop windows and installers, a UI kit with themes and motion, Trusted Types, encrypted fields and a threat model, about 2.5× faster serving, Python 3.15 and free-threaded builds, a much richer MCP server; 0.6.1: error messages with code frames and suggestions, `pyweb doctor`, dev reloads that keep page state, 4× faster reloads and tests |
|
|
580
|
+
| 0.6 | Teams with roles, invitations and tenant-scoped Models (Postgres row-level security), API tokens and OpenAPI, webhooks, installable apps with Web Push, desktop windows and installers, a UI kit with themes and motion, Trusted Types, encrypted fields and a threat model, about 2.5× faster serving, Python 3.15 and free-threaded builds, a much richer MCP server; 0.6.1: error messages with code frames and suggestions, `pyweb doctor`, dev reloads that keep page state, 4× faster reloads and tests; 0.6.2: the dev loop (hot reload in place with component state, Alt-click to source, Fix/Edit with AI, a time-travel timeline, `pyweb dev --share`, a guided `pyweb new`) |
|
|
570
581
|
| 0.5 | Models with migrations, forms, auth kit with passkeys, OAuth and an admin, row-level live updates over WebSockets on PyWeb's own server, durable jobs and schedules, `pyweb deploy` for docker, compose, k8s, fly, render and railway, logs, metrics, tracing and a dev toolbar, data-aware editor and MCP tools, the `saas` template, `pyweb upgrade` |
|
|
571
582
|
| 0.4 | npm packages without Node, layouts and client-side navigation, streaming server functions and `<Markdown>` for AI apps, live queries, page head tags, `.pyweb` error pages; 0.4.1: richer MCP tools; 0.4.2: VS Code run, new-app and MCP commands; 0.4.3: fixes, security hardening, gzip and caching; 0.4.4: key rotation, session revocation, typed arguments, Redis-shared limits, `check --production` |
|
|
572
583
|
| 0.3 | Hydration, live updates (SSE), multi-file apps, language server + VS Code extension, browser playground, faster rendering, screenshot/test MCP tools |
|
|
@@ -326,6 +326,17 @@ develop a toolbar on every page shows each request's queries and flags
|
|
|
326
326
|
N+1 loops. [Scaling & deploying →](docs/25-scaling.md) ·
|
|
327
327
|
[Logs, metrics & tracing →](docs/26-observability.md)
|
|
328
328
|
|
|
329
|
+
### A dev loop that keeps up
|
|
330
|
+
|
|
331
|
+
Save, and the open page changes in place: no reload, and every page and
|
|
332
|
+
component keeps its state. Hold Alt and click any element to open its
|
|
333
|
+
line in your editor; Alt+Shift-click to change it with AI, and error
|
|
334
|
+
pages have **Fix with AI** (your assistant's model through MCP, or your
|
|
335
|
+
own API key), with a diff to apply and Undo. The toolbar's timeline
|
|
336
|
+
steps back through every change and what caused it, and
|
|
337
|
+
`pyweb dev --share` gives someone a link to try what you're building.
|
|
338
|
+
[The dev loop →](docs/35-dev-loop.md)
|
|
339
|
+
|
|
329
340
|
### Small, fast and boring to run
|
|
330
341
|
|
|
331
342
|
- **Every page is server-rendered**: complete HTML on first paint, then
|
|
@@ -368,7 +379,7 @@ it runs the real PyWeb, server functions included, in your browser.
|
|
|
368
379
|
pip install pyweb-stack
|
|
369
380
|
pyweb new myapp --template saas # saas | desktop | blank | counter | todo | blog | auth | chat | ai-chat
|
|
370
381
|
cd myapp
|
|
371
|
-
pyweb dev app.pyweb #
|
|
382
|
+
pyweb dev app.pyweb # saves update the open page in place, state kept
|
|
372
383
|
```
|
|
373
384
|
|
|
374
385
|
Then:
|
|
@@ -404,7 +415,7 @@ The [architecture overview](ARCHITECTURE.md) goes through each stage.
|
|
|
404
415
|
|
|
405
416
|
## Build it with AI
|
|
406
417
|
|
|
407
|
-
PyWeb ships an MCP server with
|
|
418
|
+
PyWeb ships an MCP server with 31 tools, so AI assistants can do what
|
|
408
419
|
you'd do: scaffold an app, search the docs, check each edit (errors come
|
|
409
420
|
back as line numbers with fix hints), build with the UI kit and a theme,
|
|
410
421
|
render pages and call server functions as any signed-in member, look at
|
|
@@ -492,7 +503,7 @@ Upgrade with `pip install -U pyweb-stack`.
|
|
|
492
503
|
|
|
493
504
|
| Version | Highlights |
|
|
494
505
|
|---|---|
|
|
495
|
-
| 0.6 | Teams with roles, invitations and tenant-scoped Models (Postgres row-level security), API tokens and OpenAPI, webhooks, installable apps with Web Push, desktop windows and installers, a UI kit with themes and motion, Trusted Types, encrypted fields and a threat model, about 2.5× faster serving, Python 3.15 and free-threaded builds, a much richer MCP server; 0.6.1: error messages with code frames and suggestions, `pyweb doctor`, dev reloads that keep page state, 4× faster reloads and tests |
|
|
506
|
+
| 0.6 | Teams with roles, invitations and tenant-scoped Models (Postgres row-level security), API tokens and OpenAPI, webhooks, installable apps with Web Push, desktop windows and installers, a UI kit with themes and motion, Trusted Types, encrypted fields and a threat model, about 2.5× faster serving, Python 3.15 and free-threaded builds, a much richer MCP server; 0.6.1: error messages with code frames and suggestions, `pyweb doctor`, dev reloads that keep page state, 4× faster reloads and tests; 0.6.2: the dev loop (hot reload in place with component state, Alt-click to source, Fix/Edit with AI, a time-travel timeline, `pyweb dev --share`, a guided `pyweb new`) |
|
|
496
507
|
| 0.5 | Models with migrations, forms, auth kit with passkeys, OAuth and an admin, row-level live updates over WebSockets on PyWeb's own server, durable jobs and schedules, `pyweb deploy` for docker, compose, k8s, fly, render and railway, logs, metrics, tracing and a dev toolbar, data-aware editor and MCP tools, the `saas` template, `pyweb upgrade` |
|
|
497
508
|
| 0.4 | npm packages without Node, layouts and client-side navigation, streaming server functions and `<Markdown>` for AI apps, live queries, page head tags, `.pyweb` error pages; 0.4.1: richer MCP tools; 0.4.2: VS Code run, new-app and MCP commands; 0.4.3: fixes, security hardening, gzip and caching; 0.4.4: key rotation, session revocation, typed arguments, Redis-shared limits, `check --production` |
|
|
498
509
|
| 0.3 | Hydration, live updates (SSE), multi-file apps, language server + VS Code extension, browser playground, faster rendering, screenshot/test MCP tools |
|
|
@@ -177,6 +177,21 @@ websites in the user's browser.
|
|
|
177
177
|
| T | An assistant's "read-only" SQL changing data | One statement, write keywords refused (even in a CTE), a read-only transaction that is rolled back; secrets redacted | `test_dx_v5.py::test_mcp_data_tools` |
|
|
178
178
|
| T | Malformed tool arguments reaching app code | Arguments are checked against each tool's schema before it runs | `test_mcp_v6.py::test_arguments_are_checked_before_the_tool_runs` |
|
|
179
179
|
|
|
180
|
+
## 7d. The dev loop: sharing, AI help, open in editor
|
|
181
|
+
|
|
182
|
+
`pyweb dev` only. Its tools change files, open programs and show request data, so they are for the
|
|
183
|
+
person running it, on that machine: a request must come from a loopback address and name a loopback
|
|
184
|
+
`Host` (a tunnel's or the LAN's requests don't), and writes must be same-origin.
|
|
185
|
+
|
|
186
|
+
| | Threat | Mitigation | Proof |
|
|
187
|
+
|---|---|---|---|
|
|
188
|
+
| S | Someone who learns the `--share` address (or guesses the tunnel name) using the app | Every request through the tunnel needs a cookie (`HttpOnly`, `Secure`, `SameSite=Lax`) set by the printed link's random token; forged cookies are refused | `test_share.py::test_the_link_sets_a_cookie_and_nothing_else_gets_in`, `test_share.py::test_pyweb_dev_share_end_to_end` |
|
|
189
|
+
| E/I | A share-link guest (or someone on the LAN) using the developer's tools: the toolbar's SQL and requests, the agent endpoints, AI fixes, opening files, saving the theme | Guests get the app and hot reload only; every `/__pyweb/dev/` endpoint but change polling answers 403 to them, and their pages carry no toolbar or tool scripts | `test_share.py::test_guests_get_the_app_and_hot_reload_only`, `test_share.py::test_you_still_get_everything_and_the_lan_gets_hot_reload`, `test_share.py::test_a_compile_error_page_for_guests_has_no_tools` |
|
|
190
|
+
| T | A web page making "Fix with AI" or "Edit with AI" write to the app | Same-origin, loopback-only POSTs; the answer is compiled first, applied only on Apply, only to `.pyweb` files in the app, refused if the file changed since; the old version is kept for Undo | `test_assist.py::test_endpoints_refuse_other_sites_and_other_machines`, `test_assist.py::test_a_change_that_does_not_compile_is_refused`, `test_assist.py::test_apply_refuses_when_the_file_changed_since`, `test_assist.py::test_only_app_files_are_targets` |
|
|
191
|
+
| I | Credentials in the app's source sent to a model | Known key formats and random-looking secret values are replaced by placeholders before sending and put back in the answer | `test_assist.py::test_secrets_are_replaced_before_sending_and_put_back`, `test_assist.py::test_the_connected_assistant_answers_through_mcp_sampling` |
|
|
192
|
+
| E | Alt-click "open in editor" used to open arbitrary files or run commands | Only `.pyweb` files inside the app folder or the UI kit; the editor gets an argument list, never a shell; same-origin and loopback only | `test_devopen.py::test_only_app_and_kit_files_resolve`, `e2e/test_dev_inspect_browser.py::test_open_refuses_cross_site_and_outside_files` |
|
|
193
|
+
| I | Dev-only markers or scripts in production pages | `data-pw-src` markers come from dev compiles only; the dev scripts are added by the dev server only | `test_devopen.py::test_other_builds_have_no_marks`, `test_trusted_types.py::test_dev_scripts_never_ship_in_built_pages` |
|
|
194
|
+
|
|
180
195
|
## 8. The release
|
|
181
196
|
|
|
182
197
|
| | Threat | Mitigation | Proof |
|
|
@@ -43,6 +43,10 @@ Around that core, PyWeb has what a product needs, in the same file:
|
|
|
43
43
|
([Installable apps](31-pwa-push.md), [Desktop apps](32-desktop.md))
|
|
44
44
|
- **Webhooks and files**: signed webhooks in and out, and unused uploads
|
|
45
45
|
cleaned up. ([Webhooks & files](33-webhooks-files.md))
|
|
46
|
+
- **A dev loop that keeps up**: saves update the open page in place with
|
|
47
|
+
its state kept; Alt-click opens any element's source; Fix with AI and
|
|
48
|
+
Edit with AI; a time-travel timeline; `pyweb dev --share`.
|
|
49
|
+
([The dev loop](35-dev-loop.md))
|
|
46
50
|
- **Shipping**: `pyweb deploy` writes Docker, Compose, Kubernetes, Fly,
|
|
47
51
|
Render or Railway files; logs, metrics and traces come built in.
|
|
48
52
|
([Scaling & deploying](25-scaling.md))
|
|
@@ -20,9 +20,14 @@ pip install "pyweb-stack[all]" # optional: Postgres, MySQL, Redis, argon2, O
|
|
|
20
20
|
```bash
|
|
21
21
|
pyweb new hello
|
|
22
22
|
cd hello
|
|
23
|
-
pyweb dev app.pyweb # http://localhost:8000
|
|
23
|
+
pyweb dev app.pyweb # opens http://localhost:8000; saves update the page in place
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
+
(`pyweb new` with no name asks for one, a starter and colours.) Save a
|
|
27
|
+
change and the open page updates without losing its state; hold Alt and
|
|
28
|
+
click anything to open its line in your editor. [The dev loop](35-dev-loop.md)
|
|
29
|
+
has the rest: Fix with AI, time travel and sharing with `--share`.
|
|
30
|
+
|
|
26
31
|
`pyweb new` writes `app.pyweb`, a starter test (`test_app.py`) and
|
|
27
32
|
`AGENTS.md`/`CLAUDE.md` (instructions for AI coding agents). Add
|
|
28
33
|
`--template saas` for a small product with teams, accounts, a database,
|
|
@@ -107,6 +112,7 @@ ships it. Then, depending on what you're building:
|
|
|
107
112
|
- work that runs later or on a schedule: [Background jobs](24-jobs.md);
|
|
108
113
|
- teams that share data: [Teams & organizations](30-teams.md);
|
|
109
114
|
- components, a theme and animation: [UI kit & motion](34-ui-and-motion.md);
|
|
115
|
+
- everything `pyweb dev` does for you: [The dev loop](35-dev-loop.md);
|
|
110
116
|
- an app people install, or a desktop app: [Installable apps & push](31-pwa-push.md),
|
|
111
117
|
[Desktop apps](32-desktop.md);
|
|
112
118
|
- several pages with a shared header: [Layouts & navigation](19-layouts-navigation.md);
|
|
@@ -1,15 +1,21 @@
|
|
|
1
1
|
# Command line
|
|
2
2
|
|
|
3
3
|
```text
|
|
4
|
-
pyweb new NAME [--template T]
|
|
4
|
+
pyweb new [NAME] [--template T] [--theme PRESET]
|
|
5
|
+
create NAME/ with app.pyweb, test_app.py, AGENTS.md, CLAUDE.md, static/;
|
|
5
6
|
T = blank | counter (default) | todo | blog | saas | auth | chat | ai-chat | desktop
|
|
6
|
-
(apps with a database also get migrations/0001_initial.py and a README)
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
7
|
+
(apps with a database also get migrations/0001_initial.py and a README);
|
|
8
|
+
PRESET = indigo | zinc | slate | rose | ocean | violet | emerald | amber
|
|
9
|
+
(written into App(theme=...)); with no NAME, in a terminal, it asks
|
|
10
|
+
pyweb dev FILE [--port 8000] [--host 127.0.0.1] [--no-reload] [--no-open] [--share]
|
|
11
|
+
development server (see The dev loop): saves update the open page
|
|
12
|
+
in place, keeping page and component state (?fresh starts over);
|
|
13
|
+
Alt-click opens an element's line in your editor, Alt+Shift-click
|
|
14
|
+
changes it with AI, error pages have Fix with AI; the dev toolbar
|
|
15
|
+
(requests, SQL, jobs, emails, theme, motion, a time-travel timeline);
|
|
16
|
+
background jobs in the same process; opens the browser (not in CI,
|
|
17
|
+
over SSH or with --no-open); --host 0.0.0.0 prints the network URL
|
|
18
|
+
with a QR code; --share prints a temporary public link; writes
|
|
13
19
|
.pyweb/dev.json so `pyweb mcp` can see its errors and logs
|
|
14
20
|
pyweb check FILE [--production] [--strict]
|
|
15
21
|
compile, security and accessibility checks; --production also checks
|
|
@@ -11,9 +11,9 @@ can decide up front whether they matter for your app.
|
|
|
11
11
|
- **npm packages must ship ES modules.** CommonJS-only packages and
|
|
12
12
|
packages built on Node.js modules can't run in the browser; see
|
|
13
13
|
[npm packages](18-npm-packages.md).
|
|
14
|
-
- **
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
- **Time travel moves the page's state only.** The dev timeline puts the
|
|
15
|
+
page's and components' variables back; data on the server isn't
|
|
16
|
+
rolled back.
|
|
17
17
|
- **SQLite is for one machine.** Several processes on one host share it
|
|
18
18
|
fine, but more than one server needs Postgres or MySQL;
|
|
19
19
|
`pyweb check --production` and `pyweb deploy` say so.
|
|
@@ -36,7 +36,11 @@ can decide up front whether they matter for your app.
|
|
|
36
36
|
faster serving, Python 3.15 and free-threaded builds, and a much richer
|
|
37
37
|
MCP server. 0.6.1 added error messages with code frames and
|
|
38
38
|
suggestions, `pyweb doctor`, dev reloads that keep page state, and 4×
|
|
39
|
-
faster reloads and tests.
|
|
39
|
+
faster reloads and tests. 0.6.2 made the [dev loop](35-dev-loop.md):
|
|
40
|
+
saves update the page in place with component state kept, Alt-click
|
|
41
|
+
opens any element's source, Fix with AI and Edit with AI, a
|
|
42
|
+
time-travel timeline, `pyweb dev --share`, a faster first start and a
|
|
43
|
+
guided `pyweb new`.
|
|
40
44
|
- **0.5**: production apps. Models with relations and migrations, forms
|
|
41
45
|
checked in the browser and on the server, an auth kit (passwords,
|
|
42
46
|
passkeys, emailed links, OAuth, two-step sign-in, an admin), row-level
|
|
@@ -67,8 +71,8 @@ Planned, roughly in order. Nothing here is promised for a date.
|
|
|
67
71
|
1. **1.0**: the API, RPC protocol and CLI frozen under semantic versioning,
|
|
68
72
|
after 0.6 has been used in production for a while.
|
|
69
73
|
2. **File uploads from the playground and the admin.**
|
|
70
|
-
3. **
|
|
71
|
-
|
|
74
|
+
3. **Fix with AI in the editor**, from the same proposals the dev page
|
|
75
|
+
shows (diff, apply, undo), through the VS Code extension.
|
|
72
76
|
|
|
73
77
|
Ideas, bug reports and pull requests are welcome; see
|
|
74
78
|
[CONTRIBUTING.md](https://github.com/MaanavKrishna/PyWeb/blob/main/CONTRIBUTING.md).
|
|
@@ -108,6 +108,7 @@ tool runs. Slow tools (tests, screenshots, profiling) report progress.
|
|
|
108
108
|
| `pyweb_dev` | Whether `pyweb dev` is running for this app: its URL, whether the app compiles right now (with the error and traceback), its pages and recent 500 errors. |
|
|
109
109
|
| `pyweb_doctor` | Project health, as `pyweb doctor`: each check with `status` (ok, warn, fail), `detail` and a `fix`; `production=true` adds the production settings. |
|
|
110
110
|
| `pyweb_logs` | The dev server's log lines (warnings, errors with tracebacks, slow queries, job failures); `since` returns only newer ones. |
|
|
111
|
+
| `pyweb_assist_requests` | What the developer asked for from the page (Fix with AI on an error page, Alt+Shift-click → Edit with AI) while no AI was set up to answer: the file, the error or the element's line, and the instruction. |
|
|
111
112
|
|
|
112
113
|
When `pyweb dev` starts, it writes `.pyweb/dev.json` next to the app: its
|
|
113
114
|
address and a random token, readable only by you (the folder has its own
|
|
@@ -149,6 +150,7 @@ Prompts are step-by-step workflows that name the tools to use:
|
|
|
149
150
|
| `design_pass` | Kit components, a theme and motion, reviewed in light, dark and on a phone. |
|
|
150
151
|
| `fix_failing_test` | Reproduce, find the cause in the app, fix, re-run. |
|
|
151
152
|
| `debug_page` | Errors, logs, requests and a profile for one page. |
|
|
153
|
+
| `fix_errors` | Check, fix and test in a loop until the app compiles cleanly and the tests pass; first does what was asked from the page. |
|
|
152
154
|
|
|
153
155
|
### What a session looks like
|
|
154
156
|
|
|
@@ -172,6 +174,24 @@ Prompts are step-by-step workflows that name the tools to use:
|
|
|
172
174
|
click around, the assistant reads the error with `pyweb_dev` and
|
|
173
175
|
`pyweb_logs` and fixes it.
|
|
174
176
|
|
|
177
|
+
### Fix with AI from the page (sampling)
|
|
178
|
+
|
|
179
|
+
`pyweb dev` puts a **Fix with AI** button on error pages, and
|
|
180
|
+
Alt+Shift-click asks for a change to any element (see
|
|
181
|
+
[The dev loop](35-dev-loop.md#fix-with-ai-and-edit-with-ai)). When your
|
|
182
|
+
assistant's app supports MCP **sampling** (the client lets a server ask
|
|
183
|
+
its model a question, after you approve), `pyweb mcp` answers those
|
|
184
|
+
requests with your assistant's own model: it notices the client offers
|
|
185
|
+
sampling when it connects, finds the running `pyweb dev` through
|
|
186
|
+
`.pyweb/dev.json`, and hands each request over. The page shows the
|
|
187
|
+
answer as a diff to apply or discard. This works over stdio (`pyweb
|
|
188
|
+
mcp`); set `PYWEB_APP` when the app file isn't `app.pyweb` in the folder
|
|
189
|
+
the assistant starts in.
|
|
190
|
+
|
|
191
|
+
Assistants without sampling see the same requests with
|
|
192
|
+
`pyweb_assist_requests` and can do them with their own file tools; the
|
|
193
|
+
`fix_errors` prompt starts there.
|
|
194
|
+
|
|
175
195
|
`pyweb_render`, `pyweb_call`, `pyweb_screenshot`, `pyweb_review`,
|
|
176
196
|
`pyweb_profile`, `pyweb_webhook_send`, `pyweb_jobs` and `pyweb_test` run
|
|
177
197
|
your app's code, as `pyweb dev` or `pytest` would; `pyweb_packages`
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# The dev loop
|
|
2
|
+
|
|
3
|
+
`pyweb dev app.pyweb` is where you spend your day, so it does a lot for
|
|
4
|
+
you: saves show up in the open page without a reload, any element opens
|
|
5
|
+
its own line in your editor, errors come with a "Fix with AI" button, you
|
|
6
|
+
can step back through what the page did, and a link lets someone else
|
|
7
|
+
try what you're building. Everything on this page exists only under
|
|
8
|
+
`pyweb dev`: production builds, `pyweb serve` and `TestClient` are
|
|
9
|
+
unchanged.
|
|
10
|
+
|
|
11
|
+
## Your first run
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pyweb new # in a terminal: asks for a name, a starter and colours
|
|
15
|
+
cd my-app
|
|
16
|
+
pyweb dev app.pyweb # opens the browser
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
`pyweb new` with no name asks three questions: the folder name, a
|
|
20
|
+
starter (each with a line saying what it is) and a colour preset, which
|
|
21
|
+
it writes into `App(theme=Theme(...))`. `pyweb new shop --template todo
|
|
22
|
+
--theme rose` does the same without asking.
|
|
23
|
+
|
|
24
|
+
`pyweb dev` opens your browser (not in CI, over SSH, or with
|
|
25
|
+
`--no-open`) and, the first time a project runs, shows a short Welcome
|
|
26
|
+
panel: what to try, and the snippet that connects your AI assistant.
|
|
27
|
+
**Got it** hides it for good (`PYWEB_WELCOME=0` turns it off).
|
|
28
|
+
|
|
29
|
+
## Saves update the page in place
|
|
30
|
+
|
|
31
|
+
Change some markup, a style or a function and save. The open page
|
|
32
|
+
updates in about a tenth of a second, without a reload and without a
|
|
33
|
+
flash:
|
|
34
|
+
|
|
35
|
+
- **page and component state stays.** Every page variable and every
|
|
36
|
+
component's variables keep their values; two `<Counter />`s keep their
|
|
37
|
+
own counts. A variable you renamed, or whose type changed (a number
|
|
38
|
+
that is now a string), starts over.
|
|
39
|
+
- **form fields, scroll position, focus and the text selection stay**,
|
|
40
|
+
for fields that have an `id` or a `name`.
|
|
41
|
+
- **a stylesheet-only save** swaps that stylesheet and nothing else.
|
|
42
|
+
|
|
43
|
+
When the page's structure changed too much to swap in place (a new
|
|
44
|
+
layout, different npm packages), it reloads, and the same state comes
|
|
45
|
+
back. After a compile error the browser shows the error; fix it, save,
|
|
46
|
+
and you're back where you were, state included. Add `?fresh` to the URL
|
|
47
|
+
to start over every time.
|
|
48
|
+
|
|
49
|
+
The first start is fast too: parse results and compiled code are kept in
|
|
50
|
+
`~/.cache/pyweb` (`PYWEB_CACHE_DIR` to move it, `PYWEB_CACHE=0` to turn
|
|
51
|
+
it off), keyed by each file's contents and the PyWeb and Python versions.
|
|
52
|
+
|
|
53
|
+
## Alt-click to open the source
|
|
54
|
+
|
|
55
|
+
Hold **Alt** (Option on a Mac) and point at anything: it is outlined,
|
|
56
|
+
with the components it sits in and its file and line. **Alt-click** it
|
|
57
|
+
to open that line in your editor. It also opens the **inspector**: the
|
|
58
|
+
chain of components around the element, each with its live variables.
|
|
59
|
+
Type a new value (JSON: `3`, `"hi"`, `[1, 2]`) and the page follows.
|
|
60
|
+
|
|
61
|
+
The editor is `$PYWEB_EDITOR` when you set it, a command with `{file}`
|
|
62
|
+
and `{line}` in it:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
export PYWEB_EDITOR='code -g {file}:{line}' # VS Code
|
|
66
|
+
export PYWEB_EDITOR='idea --line {line} {file}' # JetBrains
|
|
67
|
+
export PYWEB_EDITOR='nvim +{line} {file}' # in a terminal editor, use a new window
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Otherwise the first of VS Code (`code`), Cursor, Windsurf, Zed and
|
|
71
|
+
Sublime Text found on your `PATH`; with none of them, the inspector shows
|
|
72
|
+
a `vscode://` link. Elements that come from the UI kit open the line in
|
|
73
|
+
your app that uses them.
|
|
74
|
+
|
|
75
|
+
## Fix with AI, and Edit with AI
|
|
76
|
+
|
|
77
|
+
When the app doesn't compile, or a page fails with an exception, the
|
|
78
|
+
error page has a **Fix with AI** button. **Alt+Shift-click** any element
|
|
79
|
+
to say what should change ("make this a card with a delete button").
|
|
80
|
+
|
|
81
|
+
Either way you get a **diff** of one file in your app, already compiled:
|
|
82
|
+
a change that doesn't compile is shown but can't be applied. **Apply**
|
|
83
|
+
writes it (only if the file hasn't changed since), the page updates in
|
|
84
|
+
place, and **Undo** puts the old version back (every replaced version is
|
|
85
|
+
also kept in `.pyweb/history/`).
|
|
86
|
+
|
|
87
|
+
Who answers, in this order:
|
|
88
|
+
|
|
89
|
+
1. **Your assistant, through `pyweb mcp`.** If the assistant's app
|
|
90
|
+
supports MCP *sampling*, `pyweb mcp` hands the request to it: its own
|
|
91
|
+
model does the work, and the app asks you before each request.
|
|
92
|
+
2. **An API key** in the environment `pyweb dev` runs in:
|
|
93
|
+
`ANTHROPIC_API_KEY`, or `OPENAI_API_KEY` / `OPENAI_BASE_URL` (any
|
|
94
|
+
OpenAI-compatible server, Ollama included), with `AI_MODEL` to choose
|
|
95
|
+
the model.
|
|
96
|
+
3. **Nobody yet.** The page gives you the prompt to copy into any
|
|
97
|
+
assistant, and a connected assistant can pick the request up with the
|
|
98
|
+
`pyweb_assist_requests` tool (the `fix_errors` prompt starts there).
|
|
99
|
+
|
|
100
|
+
What's sent is the file and the error (or your instruction). Credentials
|
|
101
|
+
in them, in the formats `pyweb check` knows and random-looking values in
|
|
102
|
+
secret-named variables, are replaced by placeholders before sending and
|
|
103
|
+
put back in the answer.
|
|
104
|
+
|
|
105
|
+
## Time travel
|
|
106
|
+
|
|
107
|
+
The toolbar's **Timeline** tab (the ⚡ badge, bottom left) records every
|
|
108
|
+
change to the page's variables with what caused it: a click on which
|
|
109
|
+
button, typing in which field, Enter, a server function call. Each event
|
|
110
|
+
is one step. Drag the slider, press **Step back** or click a step and
|
|
111
|
+
every variable goes back to its value then; the page is paused while you
|
|
112
|
+
look (clicks and typing are held back). **Resume** puts the latest values
|
|
113
|
+
back. Only the page's own state moves: data on the server isn't rolled
|
|
114
|
+
back. The last 500 changes are kept.
|
|
115
|
+
|
|
116
|
+
## On your phone, and with someone else
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
pyweb dev app.pyweb --host 0.0.0.0 # prints the network URL and a QR code
|
|
120
|
+
pyweb dev app.pyweb --share # a temporary public link (and a QR code)
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
`--host 0.0.0.0` listens on your network: scan the QR code with a phone
|
|
124
|
+
on the same Wi-Fi.
|
|
125
|
+
|
|
126
|
+
`--share` starts a tunnel, with `cloudflared` when it's installed or
|
|
127
|
+
`ssh` to localhost.run otherwise, and prints a link like
|
|
128
|
+
`https://quiet-fox-12.trycloudflare.com/__pyweb/share?t=…`. Anyone with
|
|
129
|
+
the link can use the app and sees your saves live. Nobody gets in with
|
|
130
|
+
the address alone: the link's token sets a cookie, and every other
|
|
131
|
+
request through the tunnel is refused. The link works until you stop
|
|
132
|
+
`pyweb dev`.
|
|
133
|
+
|
|
134
|
+
People on the network or coming through a share link get the app and
|
|
135
|
+
hot reload, never your tools: no toolbar (it shows SQL and request
|
|
136
|
+
data), inspector, AI help, editor opening or theme saving. Those
|
|
137
|
+
answer only requests from your machine to `localhost`.
|
|
138
|
+
|
|
139
|
+
## Security limits
|
|
140
|
+
|
|
141
|
+
| Feature | Who can use it |
|
|
142
|
+
|---|---|
|
|
143
|
+
| Hot reload, the error page | Everyone who can open the page |
|
|
144
|
+
| Toolbar, timeline, inspector, Alt-click, Fix/Edit with AI, theme saving, Welcome | You: a request from this machine, to a loopback address, from the page itself |
|
|
145
|
+
| Files AI may change | `.pyweb` files inside the app folder; only after Apply; never if the file changed since |
|
|
146
|
+
| Files Alt-click may open | `.pyweb` files in the app folder or in PyWeb's UI kit |
|
|
147
|
+
| `--share` guests | The app, with the link's cookie; nothing under `/__pyweb/dev/` but change polling |
|
|
148
|
+
| `pyweb mcp`'s view of the dev server | With the token in `.pyweb/dev.json` (readable by you only), from this machine |
|
|
149
|
+
|
|
150
|
+
The [threat model](https://github.com/MaanavKrishna/PyWeb/blob/main/THREAT_MODEL.md) lists each of these with the tests
|
|
151
|
+
that prove it.
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "pyweb-stack"
|
|
7
|
-
version = "0.6.
|
|
7
|
+
version = "0.6.2"
|
|
8
8
|
description = "Full-stack web apps in one Python file: server-rendered pages, reactive browser UI, typed RPC."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "MIT"
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""The `pyweb new` template names (kept apart so the CLI can list them without loading the MCP server)."""
|
|
2
|
+
|
|
3
|
+
TEMPLATES = ("blank", "counter", "todo", "blog", "saas", "auth", "chat", "ai-chat", "desktop")
|
|
4
|
+
|
|
5
|
+
# The colour presets `pyweb new --theme` offers (pyweb.ui.theme.PRESETS has their values; a test keeps the two in step).
|
|
6
|
+
THEME_PRESETS = ("indigo", "zinc", "slate", "rose", "ocean", "violet", "emerald", "amber")
|
|
7
|
+
|
|
8
|
+
DESCRIPTIONS = {
|
|
9
|
+
"blank": "one empty page",
|
|
10
|
+
"counter": "a page with a button: the smallest app with state",
|
|
11
|
+
"todo": "a list you add to and tick off, with the UI kit",
|
|
12
|
+
"blog": "pages with routes, a database and server functions",
|
|
13
|
+
"saas": "teams, sign-in, data, background jobs and an admin",
|
|
14
|
+
"auth": "accounts: sign up, sign in, passkeys",
|
|
15
|
+
"chat": "a live chat room (realtime)",
|
|
16
|
+
"ai-chat": "a streaming chat with an AI model",
|
|
17
|
+
"desktop": "a notes app in its own window",
|
|
18
|
+
}
|
|
@@ -51,6 +51,8 @@ def _module_code(source, filename, infos):
|
|
|
51
51
|
import hashlib
|
|
52
52
|
key = (filename, hashlib.sha256(source.encode()).hexdigest(), tuple(getattr(i, "name", "") for i in infos))
|
|
53
53
|
code = _CODE.get(key)
|
|
54
|
+
if code is None:
|
|
55
|
+
code = _disk_code(key)
|
|
54
56
|
if code is None:
|
|
55
57
|
py_source, _ui = P.split_sources(source)
|
|
56
58
|
tree = ast.parse(py_source, filename=filename)
|
|
@@ -60,9 +62,32 @@ def _module_code(source, filename, infos):
|
|
|
60
62
|
if len(_CODE) > 256:
|
|
61
63
|
_CODE.clear()
|
|
62
64
|
_CODE[key] = code
|
|
65
|
+
_disk_code(key, code)
|
|
66
|
+
elif key not in _CODE:
|
|
67
|
+
_CODE[key] = code
|
|
63
68
|
return code
|
|
64
69
|
|
|
65
70
|
|
|
71
|
+
def _disk_code(key, code=None):
|
|
72
|
+
"""Read (or with ``code``, write) a module's code object in the on-disk cache."""
|
|
73
|
+
import marshal
|
|
74
|
+
|
|
75
|
+
from .compiler import diskcache
|
|
76
|
+
if not diskcache.enabled():
|
|
77
|
+
return None
|
|
78
|
+
k = diskcache.key("code", key[0], key[1], *key[2])
|
|
79
|
+
if code is not None:
|
|
80
|
+
diskcache.put("code", k, marshal.dumps(code))
|
|
81
|
+
return code
|
|
82
|
+
data = diskcache.get("code", k)
|
|
83
|
+
if data is None:
|
|
84
|
+
return None
|
|
85
|
+
try:
|
|
86
|
+
return marshal.loads(data)
|
|
87
|
+
except Exception: # noqa: BLE001 - a corrupt entry: compile again
|
|
88
|
+
return None
|
|
89
|
+
|
|
90
|
+
|
|
66
91
|
def _kit_markup(path):
|
|
67
92
|
"""Whether a component file is kit markup (a component copied with ``pyweb ui add``)."""
|
|
68
93
|
try:
|
|
@@ -75,7 +100,7 @@ def _kit_markup(path):
|
|
|
75
100
|
|
|
76
101
|
class LoadedApp:
|
|
77
102
|
def __init__(self, path=None, *, source=None, filename=None, asset_urls=None,
|
|
78
|
-
stylesheets=None):
|
|
103
|
+
stylesheets=None, dev=False):
|
|
79
104
|
if source is None:
|
|
80
105
|
with open(path, encoding="utf-8") as fh:
|
|
81
106
|
source = fh.read()
|
|
@@ -84,7 +109,7 @@ class LoadedApp:
|
|
|
84
109
|
self.source = source
|
|
85
110
|
self.asset_urls = dict(asset_urls or {})
|
|
86
111
|
self.extra_stylesheets = list(stylesheets or [])
|
|
87
|
-
self.compiled = compile_source(source, filename=self.filename)
|
|
112
|
+
self.compiled = compile_source(source, filename=self.filename, dev=dev)
|
|
88
113
|
self.ctx = self.compiled["context"]
|
|
89
114
|
# Other .pyweb files the app imports run as real modules, registered
|
|
90
115
|
# under their import name so `from widgets import Card` works.
|