tina4-python 3.13.111__tar.gz → 3.13.112__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.
- {tina4_python-3.13.111 → tina4_python-3.13.112}/PKG-INFO +1 -1
- {tina4_python-3.13.111 → tina4_python-3.13.112}/pyproject.toml +1 -1
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/__init__.py +1 -1
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/dev_admin/__init__.py +220 -165
- {tina4_python-3.13.111 → tina4_python-3.13.112}/.gitignore +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/README.md +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/CLAUDE.md +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/HtmlElement.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/Testing.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/ai/__init__.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/ai/client.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/api/__init__.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/auth/__init__.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/cache/__init__.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/cli/__init__.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/container/__init__.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/context/__init__.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/context/chunker.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/core/__init__.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/core/cache.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/core/constants.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/core/events.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/core/middleware.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/core/port_takeover.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/core/rate_limiter.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/core/request.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/core/response.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/core/router.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/core/server.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/crud/__init__.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/database/__init__.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/database/adapter.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/database/connection.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/database/database_url.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/database/firebird.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/database/mongodb.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/database/mssql.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/database/mysql.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/database/odbc.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/database/postgres.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/database/sql_translator.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/database/sqlite.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/debug/__init__.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/debug/error_overlay.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/dev_admin/metrics.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/dev_admin/plan.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/dev_admin/project_index.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/docs.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/docstore/__init__.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/dotenv/__init__.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/env.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/frond/FROND.md +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/frond/__init__.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/frond/compiler.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/frond/engine.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/frond/parser.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/gallery/auth/meta.json +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/gallery/auth/src/routes/api/gallery_auth.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/gallery/database/meta.json +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/gallery/database/src/routes/api/gallery_db.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/gallery/error-overlay/meta.json +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/gallery/error-overlay/src/routes/api/gallery_crash.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/gallery/orm/meta.json +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/gallery/orm/src/orm/Product.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/gallery/orm/src/routes/api/gallery_products.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/gallery/queue/meta.json +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/gallery/queue/src/routes/api/gallery_queue.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/gallery/rest-api/meta.json +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/gallery/rest-api/src/routes/api/gallery_hello.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/gallery/templates/meta.json +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/gallery/templates/src/routes/gallery_page.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/gallery/templates/src/templates/gallery_page.twig +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/graph/__init__.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/graph/adapter.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/graph/adapters/__init__.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/graph/adapters/arango.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/graph/adapters/bolt.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/graph/adapters/ultipa.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/graph/graph_url.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/graphql/__init__.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/i18n/__init__.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/mcp/__init__.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/mcp/protocol.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/mcp/tools.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/messenger/__init__.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/migration/__init__.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/migration/runner.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/mqtt/__init__.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/mqtt/message.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/orm/__init__.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/orm/fields.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/orm/model.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/orm/point.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/public/__feedback/widget.js +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/public/css/tina4.css +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/public/css/tina4.min.css +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/public/favicon.ico +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/public/images/logo.svg +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/public/images/tina4-logo-icon.webp +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/public/js/frond.js +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/public/js/frond.min.js +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/public/js/tina4-dev-admin.min.js +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/public/js/tina4.min.js +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/public/js/tina4js.min.js +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/public/swagger/index.html +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/public/swagger/oauth2-redirect.html +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/query_builder/__init__.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/queue/__init__.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/queue/amqp_url.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/queue/job.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/queue/kafka_backend.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/queue/lite_backend.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/queue/mongo_backend.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/queue/rabbitmq_backend.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/queue_backends/__init__.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/queue_backends/kafka_backend.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/queue_backends/mongo_backend.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/queue_backends/rabbitmq_backend.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/realtime/__init__.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/realtime/models/Attachment.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/realtime/models/Channel.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/realtime/models/ChannelMember.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/realtime/models/Message.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/realtime/models/Workspace.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/realtime/models/__init__.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/realtime/storage.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/seeder/__init__.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/service/__init__.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/session/__init__.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/session_handlers/__init__.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/session_handlers/memcached_handler.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/session_handlers/mongodb_handler.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/session_handlers/redis_handler.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/session_handlers/valkey_handler.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/sso.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/swagger/__init__.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/templates/components/crud.twig +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/templates/docker/distroless/Dockerfile +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/templates/docker/poetry/Dockerfile +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/templates/docker/python/Dockerfile +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/templates/docker/uv/Dockerfile +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/templates/errors/302.twig +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/templates/errors/401.twig +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/templates/errors/403.twig +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/templates/errors/404.twig +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/templates/errors/500.twig +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/templates/errors/502.twig +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/templates/errors/503.twig +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/templates/errors/base.twig +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/templates/frontend/README.md +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/templates/readme.md +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/test/__init__.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/test_client/__init__.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/translations/af/LC_MESSAGES/messages.mo +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/translations/af/LC_MESSAGES/messages.po +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/translations/en/LC_MESSAGES/messages.mo +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/translations/en/LC_MESSAGES/messages.po +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/translations/es/LC_MESSAGES/messages.mo +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/translations/es/LC_MESSAGES/messages.po +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/translations/fr/LC_MESSAGES/messages.mo +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/translations/fr/LC_MESSAGES/messages.po +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/translations/ja/LC_MESSAGES/messages.mo +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/translations/ja/LC_MESSAGES/messages.po +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/translations/zh/LC_MESSAGES/messages.mo +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/translations/zh/LC_MESSAGES/messages.po +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/validator/__init__.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/websocket/__init__.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/websocket/backplane.py +0 -0
- {tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/wsdl/__init__.py +0 -0
|
@@ -547,6 +547,11 @@ def get_api_handlers() -> dict:
|
|
|
547
547
|
"/__dev/api/connections/save": ("POST", _api_connections_save),
|
|
548
548
|
"/__dev/api/gallery": ("GET", _api_gallery_list),
|
|
549
549
|
"/__dev/api/gallery/deploy": ("POST", _api_gallery_deploy),
|
|
550
|
+
# Dev-toolbar assets — external CSS/JS so the injected toolbar is
|
|
551
|
+
# CSP-clean (no inline styles, handlers, or <script>) and works under
|
|
552
|
+
# a strict `default-src 'self'` (SecurityHeadersMiddleware). #115.
|
|
553
|
+
"/__dev/toolbar.css": ("GET", _api_toolbar_css),
|
|
554
|
+
"/__dev/toolbar.js": ("GET", _api_toolbar_js),
|
|
550
555
|
"/__dev/api/mtime": ("GET", _api_mtime),
|
|
551
556
|
"/__dev/api/reload": ("POST", _api_reload),
|
|
552
557
|
"/__dev/api/version-check": ("GET", _api_version_check),
|
|
@@ -1749,6 +1754,27 @@ async def _api_feedback_turn(request, response):
|
|
|
1749
1754
|
return response(raw.decode("utf-8", errors="replace"))
|
|
1750
1755
|
|
|
1751
1756
|
|
|
1757
|
+
async def _api_toolbar_css(request, response):
|
|
1758
|
+
"""Serve the dev-toolbar stylesheet (CSP-clean external asset, #115).
|
|
1759
|
+
|
|
1760
|
+
text/css so the injected toolbar carries no inline ``style=`` and renders
|
|
1761
|
+
under a strict ``default-src 'self'`` CSP. Served via the response callable's
|
|
1762
|
+
explicit content-type arg (never ``.html()``) so the toolbar is not injected
|
|
1763
|
+
into its own asset — mirrors PHP's ``/__dev/toolbar.css`` route.
|
|
1764
|
+
"""
|
|
1765
|
+
return response(toolbar_css(), 200, "text/css; charset=UTF-8")
|
|
1766
|
+
|
|
1767
|
+
|
|
1768
|
+
async def _api_toolbar_js(request, response):
|
|
1769
|
+
"""Serve the dev-toolbar script (CSP-clean external asset, #115).
|
|
1770
|
+
|
|
1771
|
+
application/javascript so every toolbar event is wired via
|
|
1772
|
+
``addEventListener`` (no inline handlers / ``<script>``) and runs under a
|
|
1773
|
+
strict ``default-src 'self'`` CSP — mirrors PHP's ``/__dev/toolbar.js`` route.
|
|
1774
|
+
"""
|
|
1775
|
+
return response(toolbar_js(), 200, "application/javascript; charset=UTF-8")
|
|
1776
|
+
|
|
1777
|
+
|
|
1752
1778
|
async def _api_feedback_widget_js(request, response):
|
|
1753
1779
|
"""Serve the widget bundle.
|
|
1754
1780
|
|
|
@@ -2232,6 +2258,17 @@ def render_dev_toolbar(method: str, path: str, matched_pattern: str,
|
|
|
2232
2258
|
Shows: Tina4 version (blue), HTTP method (green), path, matched pattern,
|
|
2233
2259
|
request ID (yellow), route count (blue), Python version, Dashboard link,
|
|
2234
2260
|
and a close button.
|
|
2261
|
+
|
|
2262
|
+
CSP-clean (tina4stack #115): the toolbar carries NO inline ``style=``, NO
|
|
2263
|
+
``onclick=``, and NO inline ``<script>`` block. Presentation lives in the
|
|
2264
|
+
external stylesheet served at ``/__dev/toolbar.css`` and every event is
|
|
2265
|
+
wired via ``addEventListener`` inside ``/__dev/toolbar.js`` — so the toolbar
|
|
2266
|
+
renders and works under the framework's strict ``default-src 'self'`` CSP
|
|
2267
|
+
(SecurityHeadersMiddleware) instead of being stripped and spewing violations.
|
|
2268
|
+
Faithful parity with PHP's ``DevAdmin::renderToolbar()``.
|
|
2269
|
+
|
|
2270
|
+
The reloader is suppressed by emitting ``data-reload="0"`` (AI/stable port,
|
|
2271
|
+
or ``TINA4_NO_RELOAD``); toolbar.js early-returns unless it reads ``"1"``.
|
|
2235
2272
|
"""
|
|
2236
2273
|
import sys
|
|
2237
2274
|
from html import escape as _html_escape
|
|
@@ -2244,190 +2281,208 @@ def render_dev_toolbar(method: str, path: str, matched_pattern: str,
|
|
|
2244
2281
|
method = _html_escape(str(method), quote=True)
|
|
2245
2282
|
path = _html_escape(str(path), quote=True)
|
|
2246
2283
|
matched_pattern = _html_escape(str(matched_pattern), quote=True)
|
|
2247
|
-
|
|
2284
|
+
request_id = _html_escape(str(request_id), quote=True)
|
|
2248
2285
|
no_reload = os.environ.get("TINA4_NO_RELOAD", "").lower() in ("true", "1", "yes") or _ai_port_ctx.get()
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2286
|
+
reload = "0" if no_reload else "1"
|
|
2287
|
+
|
|
2288
|
+
return f"""<link rel="stylesheet" href="/__dev/toolbar.css">
|
|
2289
|
+
<div id="tina4-dev-toolbar" data-reload="{reload}">
|
|
2290
|
+
<span id="tina4-ver-btn" title="Click to check for updates">Tina4 v{__version__}</span>
|
|
2291
|
+
<div id="tina4-ver-modal">
|
|
2292
|
+
<div class="t4-modal-head">
|
|
2293
|
+
<strong class="t4-modal-title">Version Info</strong>
|
|
2294
|
+
<span id="tina4-ver-close" class="t4-x">×</span>
|
|
2256
2295
|
</div>
|
|
2257
|
-
<div id="tina4-ver-body"
|
|
2258
|
-
<div>Current: <strong
|
|
2259
|
-
<div id="tina4-ver-latest"
|
|
2296
|
+
<div id="tina4-ver-body">
|
|
2297
|
+
<div>Current: <strong class="t4-ok">v{__version__}</strong></div>
|
|
2298
|
+
<div id="tina4-ver-latest" class="t4-dim">Checking for updates...</div>
|
|
2260
2299
|
</div>
|
|
2261
2300
|
</div>
|
|
2262
|
-
<span
|
|
2301
|
+
<span class="t4-green">{method}</span>
|
|
2263
2302
|
<span>{path}</span>
|
|
2264
|
-
<span
|
|
2265
|
-
<span
|
|
2266
|
-
<span
|
|
2267
|
-
<span
|
|
2268
|
-
<a href="#"
|
|
2269
|
-
<span
|
|
2303
|
+
<span class="t4-arrow">→ {matched_pattern}</span>
|
|
2304
|
+
<span class="t4-yellow">req:{request_id}</span>
|
|
2305
|
+
<span class="t4-blue">{route_count} routes</span>
|
|
2306
|
+
<span class="t4-dim">Python {python_version}</span>
|
|
2307
|
+
<a href="#" id="tina4-dash-link" class="t4-dash">Dashboard ↗</a>
|
|
2308
|
+
<span id="tina4-bar-close" class="t4-x t4-bar-close">✕</span>
|
|
2270
2309
|
</div>
|
|
2271
|
-
<script>
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2310
|
+
<script src="/__dev/toolbar.js"></script>"""
|
|
2311
|
+
|
|
2312
|
+
|
|
2313
|
+
def toolbar_css() -> str:
|
|
2314
|
+
"""CSS for the injected dev toolbar, served as an external stylesheet.
|
|
2315
|
+
|
|
2316
|
+
Extracted so the toolbar carries no inline ``style=`` and stays CSP-clean
|
|
2317
|
+
under a strict ``default-src 'self'``. Parity with PHP ``toolbarCss()`` —
|
|
2318
|
+
Python keeps its own accent (``#3572A5``) but the same class vocabulary so
|
|
2319
|
+
the JS can set state via ``className`` rather than inline styles.
|
|
2320
|
+
"""
|
|
2321
|
+
return """#tina4-dev-toolbar{position:fixed;bottom:0;left:0;right:0;background:#333;color:#fff;font-family:monospace;font-size:12px;padding:6px 16px;z-index:99999;display:flex;align-items:center;gap:16px}
|
|
2322
|
+
#tina4-dev-toolbar a{text-decoration:none}
|
|
2323
|
+
#tina4-ver-btn{color:#3572A5;font-weight:bold;cursor:pointer;text-decoration:underline dotted}
|
|
2324
|
+
#tina4-ver-modal{display:none;position:fixed;bottom:3rem;left:1rem;background:#1e1e2e;border:1px solid #3572A5;border-radius:8px;padding:16px 20px;z-index:100000;min-width:320px;box-shadow:0 8px 32px rgba(0,0,0,.5);font-family:monospace;font-size:13px;color:#cdd6f4}
|
|
2325
|
+
.t4-modal-head{display:flex;justify-content:space-between;align-items:center;margin-bottom:12px}
|
|
2326
|
+
.t4-modal-title{color:#89b4fa}
|
|
2327
|
+
#tina4-ver-body{line-height:1.8}
|
|
2328
|
+
.t4-x{cursor:pointer;color:#888}
|
|
2329
|
+
.t4-bar-close{margin-left:8px}
|
|
2330
|
+
.t4-green{color:#4caf50}
|
|
2331
|
+
.t4-dim{color:#888}
|
|
2332
|
+
.t4-arrow{color:#666}
|
|
2333
|
+
.t4-yellow{color:#ffeb3b}
|
|
2334
|
+
.t4-blue{color:#90caf9}
|
|
2335
|
+
.t4-ok{color:#a6e3a1}
|
|
2336
|
+
.t4-warn{color:#f9e2af}
|
|
2337
|
+
.t4-err{color:#f38ba8}
|
|
2338
|
+
.t4-purple{color:#cba6f7}
|
|
2339
|
+
.t4-link{color:#89b4fa}
|
|
2340
|
+
.t4-code{background:#313244;padding:2px 6px;border-radius:3px}
|
|
2341
|
+
.t4-note{margin-top:6px}
|
|
2342
|
+
.t4-dash{color:#ef9a9a;margin-left:auto;cursor:pointer}
|
|
2343
|
+
#tina4-dev-panel{position:fixed;top:3rem;left:0;right:0;bottom:2rem;z-index:99998;transition:all .2s}
|
|
2344
|
+
#tina4-dev-panel iframe{width:100%;height:100%;border:1px solid #3572A5;border-radius:.5rem;box-shadow:0 8px 32px rgba(0,0,0,.5);background:#0f172a}
|
|
2345
|
+
"""
|
|
2346
|
+
|
|
2347
|
+
|
|
2348
|
+
def toolbar_js() -> str:
|
|
2349
|
+
"""JS for the injected dev toolbar, served as an external script.
|
|
2350
|
+
|
|
2351
|
+
Wires the version-check modal, the dashboard overlay (hydrated from
|
|
2352
|
+
localStorage) and the WebSocket-primary live reloader — every event via
|
|
2353
|
+
``addEventListener``, no inline handlers — so the toolbar stays CSP-clean.
|
|
2354
|
+
The reloader only starts when the toolbar root's ``data-reload`` attribute
|
|
2355
|
+
is ``"1"``; on the AI/stable port (or with ``TINA4_NO_RELOAD``) the render
|
|
2356
|
+
emits ``"0"`` and the reloader early-returns. Parity with PHP ``toolbarJs()``.
|
|
2357
|
+
|
|
2358
|
+
The mtime-poll fallback interval honours ``TINA4_DEV_POLL_INTERVAL``.
|
|
2359
|
+
"""
|
|
2360
|
+
poll_interval_ms = int(os.environ.get("TINA4_DEV_POLL_INTERVAL", "3000"))
|
|
2361
|
+
return """(function () {
|
|
2362
|
+
var bar = document.getElementById('tina4-dev-toolbar');
|
|
2363
|
+
if (!bar) { return; }
|
|
2364
|
+
|
|
2365
|
+
var modal = document.getElementById('tina4-ver-modal');
|
|
2366
|
+
function upToDate(el, latest) {
|
|
2367
|
+
el.className = 't4-ok';
|
|
2368
|
+
el.innerHTML = 'Latest: <strong class="t4-ok">v' + latest + '</strong> — You are up to date!';
|
|
2369
|
+
}
|
|
2370
|
+
function checkVersion() {
|
|
2371
|
+
if (modal.style.display === 'block') { modal.style.display = 'none'; return; }
|
|
2372
|
+
modal.style.display = 'block';
|
|
2373
|
+
var el = document.getElementById('tina4-ver-latest');
|
|
2374
|
+
el.className = 't4-dim';
|
|
2375
|
+
el.textContent = 'Checking for updates...';
|
|
2376
|
+
fetch('/__dev/api/version-check').then(function (r) { return r.json(); }).then(function (d) {
|
|
2377
|
+
var latest = d.latest, current = d.current;
|
|
2378
|
+
if (latest === current) { upToDate(el, latest); return; }
|
|
2379
|
+
var cP = current.split('.').map(Number), lP = latest.split('.').map(Number);
|
|
2380
|
+
var isNewer = false, i, c, l;
|
|
2381
|
+
for (i = 0; i < Math.max(cP.length, lP.length); i++) { c = cP[i] || 0; l = lP[i] || 0; if (l > c) { isNewer = true; break; } if (l < c) { break; } }
|
|
2382
|
+
var isAhead = false;
|
|
2383
|
+
if (!isNewer) { for (i = 0; i < Math.max(cP.length, lP.length); i++) { var c2 = cP[i] || 0, l2 = lP[i] || 0; if (c2 > l2) { isAhead = true; break; } if (c2 < l2) { break; } } }
|
|
2384
|
+
if (isNewer) {
|
|
2385
|
+
var breaking = (lP[0] !== cP[0] || lP[1] !== cP[1]);
|
|
2386
|
+
el.className = '';
|
|
2387
|
+
el.innerHTML = 'Latest: <strong class="t4-warn">v' + latest + '</strong>';
|
|
2388
|
+
if (breaking) {
|
|
2389
|
+
el.innerHTML += '<div class="t4-err t4-note">⚠ Major/minor version change — check the <a href="https://github.com/tina4stack/tina4-python/releases" target="_blank" class="t4-link">changelog</a> for breaking changes before upgrading.</div>';
|
|
2390
|
+
} else {
|
|
2391
|
+
el.innerHTML += '<div class="t4-warn t4-note">Patch update available. Run: <code class="t4-code">pip install --upgrade tina4-python</code></div>';
|
|
2392
|
+
}
|
|
2393
|
+
} else if (isAhead) {
|
|
2394
|
+
el.className = 't4-purple';
|
|
2395
|
+
el.innerHTML = 'You are running <strong class="t4-purple">v' + current + '</strong> (ahead of PyPI <strong>v' + latest + '</strong> — not yet published).';
|
|
2396
|
+
} else {
|
|
2397
|
+
upToDate(el, latest);
|
|
2398
|
+
}
|
|
2399
|
+
}).catch(function () {
|
|
2400
|
+
el.className = 't4-err';
|
|
2401
|
+
el.textContent = 'Could not check for updates (offline?)';
|
|
2402
|
+
});
|
|
2403
|
+
}
|
|
2404
|
+
var verBtn = document.getElementById('tina4-ver-btn');
|
|
2405
|
+
if (verBtn) { verBtn.addEventListener('click', checkVersion); }
|
|
2406
|
+
var verClose = document.getElementById('tina4-ver-close');
|
|
2407
|
+
if (verClose) { verClose.addEventListener('click', function () { modal.style.display = 'none'; }); }
|
|
2408
|
+
var barClose = document.getElementById('tina4-bar-close');
|
|
2409
|
+
if (barClose) { barClose.addEventListener('click', function () { bar.style.display = 'none'; }); }
|
|
2410
|
+
|
|
2278
2411
|
var STATE_KEY = 'tina4_dev_overlay_open';
|
|
2279
|
-
function buildOverlay() {
|
|
2412
|
+
function buildOverlay() {
|
|
2280
2413
|
var c = document.createElement('div');
|
|
2281
2414
|
c.id = 'tina4-dev-panel';
|
|
2282
|
-
c.style.cssText = 'position:fixed;top:3rem;left:0;right:0;bottom:2rem;z-index:99998;transition:all 0.2s';
|
|
2283
2415
|
var f = document.createElement('iframe');
|
|
2284
2416
|
f.src = '/__dev';
|
|
2285
|
-
f.style.cssText = 'width:100%;height:100%;border:1px solid #3572A5;border-radius:0.5rem;box-shadow:0 8px 32px rgba(0,0,0,0.5);background:#0f172a';
|
|
2286
2417
|
c.appendChild(f);
|
|
2287
2418
|
document.body.appendChild(c);
|
|
2288
2419
|
return c;
|
|
2289
|
-
}
|
|
2290
|
-
|
|
2291
|
-
if (e) e.preventDefault();
|
|
2420
|
+
}
|
|
2421
|
+
function toggleOverlay(e) {
|
|
2422
|
+
if (e) { e.preventDefault(); }
|
|
2292
2423
|
var p = document.getElementById('tina4-dev-panel');
|
|
2293
|
-
if (p) {
|
|
2424
|
+
if (p) {
|
|
2294
2425
|
var hide = p.style.display !== 'none';
|
|
2295
2426
|
p.style.display = hide ? 'none' : 'block';
|
|
2296
|
-
try {
|
|
2427
|
+
try { localStorage.setItem(STATE_KEY, hide ? '0' : '1'); } catch (_) {}
|
|
2297
2428
|
return;
|
|
2298
|
-
}
|
|
2429
|
+
}
|
|
2299
2430
|
buildOverlay();
|
|
2300
|
-
try {
|
|
2301
|
-
}
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
var _t4_ws=null,_t4_poll_timer=null,_t4_mtime=null;
|
|
2326
|
-
function _t4_apply(d){{
|
|
2327
|
-
d=d||{{}};
|
|
2328
|
-
var f=d.file||'',t=d.type||'';
|
|
2329
|
-
var isCss=t==='css'||_t4_css_exts.some(function(e){{return f.endsWith(e)}});
|
|
2330
|
-
if(isCss){{
|
|
2331
|
-
var links=document.querySelectorAll('link[rel="stylesheet"]');
|
|
2332
|
-
links.forEach(function(l){{
|
|
2333
|
-
var href=l.getAttribute('href');
|
|
2334
|
-
if(href){{l.setAttribute('href',href.split('?')[0]+'?_t4='+(d.mtime||Date.now()))}}
|
|
2335
|
-
}});
|
|
2336
|
-
}}else{{
|
|
2431
|
+
try { localStorage.setItem(STATE_KEY, '1'); } catch (_) {}
|
|
2432
|
+
}
|
|
2433
|
+
var dash = document.getElementById('tina4-dash-link');
|
|
2434
|
+
if (dash) { dash.addEventListener('click', toggleOverlay); }
|
|
2435
|
+
try {
|
|
2436
|
+
if (location.pathname.indexOf('/__dev') !== 0
|
|
2437
|
+
&& localStorage.getItem(STATE_KEY) === '1'
|
|
2438
|
+
&& !document.getElementById('tina4-dev-panel')) {
|
|
2439
|
+
buildOverlay();
|
|
2440
|
+
}
|
|
2441
|
+
} catch (_) {}
|
|
2442
|
+
|
|
2443
|
+
if (bar.getAttribute('data-reload') !== '1') { return; }
|
|
2444
|
+
var cssExts = ['.css', '.scss'], debounce = null, interval = parseInt('__POLL_INTERVAL__') || 3000;
|
|
2445
|
+
var ws = null, pollTimer = null, mtime = null;
|
|
2446
|
+
function apply(d) {
|
|
2447
|
+
d = d || {};
|
|
2448
|
+
var f = d.file || '', t = d.type || '';
|
|
2449
|
+
var isCss = t === 'css' || cssExts.some(function (e) { return f.endsWith(e); });
|
|
2450
|
+
if (isCss) {
|
|
2451
|
+
document.querySelectorAll('link[rel="stylesheet"]').forEach(function (l) {
|
|
2452
|
+
var href = l.getAttribute('href');
|
|
2453
|
+
if (href) { l.setAttribute('href', href.split('?')[0] + '?_t4=' + (d.mtime || Date.now())); }
|
|
2454
|
+
});
|
|
2455
|
+
} else {
|
|
2337
2456
|
location.reload();
|
|
2338
|
-
}
|
|
2339
|
-
}
|
|
2340
|
-
function
|
|
2341
|
-
fetch('/__dev/api/mtime').then(function(r){
|
|
2342
|
-
if(
|
|
2343
|
-
if(d.mtime!==
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
if(_t4_debounce)clearTimeout(_t4_debounce);
|
|
2368
|
-
_t4_debounce=setTimeout(function(){{_t4_apply(d);}},150);
|
|
2369
|
-
}}
|
|
2370
|
-
}});
|
|
2371
|
-
_t4_ws.addEventListener('close',function(){{_t4_ws=null;_t4_startPoll();setTimeout(_t4_connect,2000);}});
|
|
2372
|
-
_t4_ws.addEventListener('error',function(){{try{{_t4_ws&&_t4_ws.close();}}catch(_){{}}}});
|
|
2373
|
-
}}
|
|
2374
|
-
_t4_connect();
|
|
2375
|
-
}})();"""}
|
|
2376
|
-
function tina4VersionModal(){{
|
|
2377
|
-
var m=document.getElementById('tina4-ver-modal');
|
|
2378
|
-
if(m.style.display==='block'){{m.style.display='none';return;}}
|
|
2379
|
-
m.style.display='block';
|
|
2380
|
-
var el=document.getElementById('tina4-ver-latest');
|
|
2381
|
-
el.innerHTML='Checking for updates...';
|
|
2382
|
-
el.style.color='#888';
|
|
2383
|
-
fetch('/__dev/api/version-check')
|
|
2384
|
-
.then(function(r){{return r.json()}})
|
|
2385
|
-
.then(function(d){{
|
|
2386
|
-
var latest=d.latest;
|
|
2387
|
-
var current=d.current;
|
|
2388
|
-
if(latest===current){{
|
|
2389
|
-
el.innerHTML='Latest: <strong style="color:#a6e3a1;">v'+latest+'</strong> — You are up to date!';
|
|
2390
|
-
el.style.color='#a6e3a1';
|
|
2391
|
-
}}else{{
|
|
2392
|
-
var cParts=current.split('.').map(Number);
|
|
2393
|
-
var lParts=latest.split('.').map(Number);
|
|
2394
|
-
var isNewer=false;
|
|
2395
|
-
for(var i=0;i<Math.max(cParts.length,lParts.length);i++){{
|
|
2396
|
-
var c=cParts[i]||0,l=lParts[i]||0;
|
|
2397
|
-
if(l>c){{isNewer=true;break;}}
|
|
2398
|
-
if(l<c)break;
|
|
2399
|
-
}}
|
|
2400
|
-
var isAhead=false;
|
|
2401
|
-
if(!isNewer){{
|
|
2402
|
-
for(var i=0;i<Math.max(cParts.length,lParts.length);i++){{
|
|
2403
|
-
var c2=cParts[i]||0,l2=lParts[i]||0;
|
|
2404
|
-
if(c2>l2){{isAhead=true;break;}}
|
|
2405
|
-
if(c2<l2)break;
|
|
2406
|
-
}}
|
|
2407
|
-
}}
|
|
2408
|
-
if(isNewer){{
|
|
2409
|
-
var breaking=(lParts[0]!==cParts[0]||lParts[1]!==cParts[1]);
|
|
2410
|
-
el.innerHTML='Latest: <strong style="color:#f9e2af;">v'+latest+'</strong>';
|
|
2411
|
-
if(breaking){{
|
|
2412
|
-
el.innerHTML+='<div style="color:#f38ba8;margin-top:6px;">⚠ Major/minor version change — check the <a href="https://github.com/tina4stack/tina4-python/releases" target="_blank" style="color:#89b4fa;">changelog</a> for breaking changes before upgrading.</div>';
|
|
2413
|
-
}}else{{
|
|
2414
|
-
el.innerHTML+='<div style="color:#f9e2af;margin-top:6px;">Patch update available. Run: <code style="background:#313244;padding:2px 6px;border-radius:3px;">pip install --upgrade tina4-python</code></div>';
|
|
2415
|
-
}}
|
|
2416
|
-
}}else if(isAhead){{
|
|
2417
|
-
el.innerHTML='You are running <strong style="color:#cba6f7;">v'+current+'</strong> (ahead of PyPI <strong>v'+latest+'</strong> — not yet published).';
|
|
2418
|
-
el.style.color='#cba6f7';
|
|
2419
|
-
}}else{{
|
|
2420
|
-
el.innerHTML='Latest: <strong style="color:#a6e3a1;">v'+latest+'</strong> — You are up to date!';
|
|
2421
|
-
el.style.color='#a6e3a1';
|
|
2422
|
-
}}
|
|
2423
|
-
}}
|
|
2424
|
-
}})
|
|
2425
|
-
.catch(function(){{
|
|
2426
|
-
el.innerHTML='Could not check for updates (offline?)';
|
|
2427
|
-
el.style.color='#f38ba8';
|
|
2428
|
-
}});
|
|
2429
|
-
}}
|
|
2430
|
-
</script>"""
|
|
2457
|
+
}
|
|
2458
|
+
}
|
|
2459
|
+
function poll() {
|
|
2460
|
+
fetch('/__dev/api/mtime').then(function (r) { return r.json(); }).then(function (d) {
|
|
2461
|
+
if (mtime === null) { mtime = d.mtime; return; }
|
|
2462
|
+
if (d.mtime !== mtime) { mtime = d.mtime; if (debounce) { clearTimeout(debounce); } debounce = setTimeout(function () { apply(d); }, 500); }
|
|
2463
|
+
}).catch(function () {});
|
|
2464
|
+
}
|
|
2465
|
+
function startPoll() { if (pollTimer) { return; } mtime = null; pollTimer = setInterval(poll, interval); }
|
|
2466
|
+
function stopPoll() { if (pollTimer) { clearInterval(pollTimer); pollTimer = null; } }
|
|
2467
|
+
function connect() {
|
|
2468
|
+
var url = (location.protocol === 'https:' ? 'wss' : 'ws') + '://' + location.host + '/__dev_reload';
|
|
2469
|
+
try { ws = new WebSocket(url); } catch (_) { startPoll(); return; }
|
|
2470
|
+
ws.addEventListener('open', function () { stopPoll(); });
|
|
2471
|
+
ws.addEventListener('message', function (ev) {
|
|
2472
|
+
var d = null;
|
|
2473
|
+
try { d = typeof ev.data === 'string' ? JSON.parse(ev.data) : null; } catch (_) {}
|
|
2474
|
+
if (!d) { return; }
|
|
2475
|
+
if (d.type === 'reload' || d.type === 'change' || d.type === 'css') {
|
|
2476
|
+
if (debounce) { clearTimeout(debounce); }
|
|
2477
|
+
debounce = setTimeout(function () { apply(d); }, 150);
|
|
2478
|
+
}
|
|
2479
|
+
});
|
|
2480
|
+
ws.addEventListener('close', function () { ws = null; startPoll(); setTimeout(connect, 2000); });
|
|
2481
|
+
ws.addEventListener('error', function () { try { ws && ws.close(); } catch (_) {} });
|
|
2482
|
+
}
|
|
2483
|
+
connect();
|
|
2484
|
+
})();
|
|
2485
|
+
""".replace("__POLL_INTERVAL__", str(poll_interval_ms))
|
|
2431
2486
|
|
|
2432
2487
|
|
|
2433
2488
|
# ── Editor API endpoints ──────────────────────────────────────
|
|
@@ -3719,4 +3774,4 @@ async def _api_docs_well_known(request, response):
|
|
|
3719
3774
|
|
|
3720
3775
|
|
|
3721
3776
|
__all__ = ["MessageLog", "RequestInspector", "BrokenTracker",
|
|
3722
|
-
"get_api_handlers", "render_dev_toolbar"]
|
|
3777
|
+
"get_api_handlers", "render_dev_toolbar", "toolbar_css", "toolbar_js"]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/gallery/error-overlay/meta.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/public/images/tina4-logo-icon.webp
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/public/js/tina4-dev-admin.min.js
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/public/swagger/oauth2-redirect.html
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/queue_backends/kafka_backend.py
RENAMED
|
File without changes
|
{tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/queue_backends/mongo_backend.py
RENAMED
|
File without changes
|
{tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/queue_backends/rabbitmq_backend.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/realtime/models/ChannelMember.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/session_handlers/memcached_handler.py
RENAMED
|
File without changes
|
{tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/session_handlers/mongodb_handler.py
RENAMED
|
File without changes
|
{tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/session_handlers/redis_handler.py
RENAMED
|
File without changes
|
{tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/session_handlers/valkey_handler.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/templates/docker/distroless/Dockerfile
RENAMED
|
File without changes
|
{tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/templates/docker/poetry/Dockerfile
RENAMED
|
File without changes
|
{tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/templates/docker/python/Dockerfile
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/translations/af/LC_MESSAGES/messages.mo
RENAMED
|
File without changes
|
{tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/translations/af/LC_MESSAGES/messages.po
RENAMED
|
File without changes
|
{tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/translations/en/LC_MESSAGES/messages.mo
RENAMED
|
File without changes
|
{tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/translations/en/LC_MESSAGES/messages.po
RENAMED
|
File without changes
|
{tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/translations/es/LC_MESSAGES/messages.mo
RENAMED
|
File without changes
|
{tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/translations/es/LC_MESSAGES/messages.po
RENAMED
|
File without changes
|
{tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/translations/fr/LC_MESSAGES/messages.mo
RENAMED
|
File without changes
|
{tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/translations/fr/LC_MESSAGES/messages.po
RENAMED
|
File without changes
|
{tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/translations/ja/LC_MESSAGES/messages.mo
RENAMED
|
File without changes
|
{tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/translations/ja/LC_MESSAGES/messages.po
RENAMED
|
File without changes
|
{tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/translations/zh/LC_MESSAGES/messages.mo
RENAMED
|
File without changes
|
{tina4_python-3.13.111 → tina4_python-3.13.112}/tina4_python/translations/zh/LC_MESSAGES/messages.po
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|