databricks-tellr-app 0.4.2.dev14__tar.gz → 0.4.2.dev16__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.
Files changed (167) hide show
  1. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/PKG-INFO +2 -1
  2. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/databricks_tellr_app.egg-info/PKG-INFO +2 -1
  3. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/databricks_tellr_app.egg-info/requires.txt +1 -0
  4. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/pyproject.toml +14 -1
  5. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/api/mcp_server.py +39 -2
  6. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/api/routes/export.py +59 -9
  7. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/services/design_system_compiler.py +71 -28
  8. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/README.md +0 -0
  9. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/databricks_tellr_app/__init__.py +0 -0
  10. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/databricks_tellr_app/run.py +0 -0
  11. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/databricks_tellr_app.egg-info/SOURCES.txt +0 -0
  12. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/databricks_tellr_app.egg-info/dependency_links.txt +0 -0
  13. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/databricks_tellr_app.egg-info/top_level.txt +0 -0
  14. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/setup.cfg +0 -0
  15. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/setup.py +0 -0
  16. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/__init__.py +0 -0
  17. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/api/__init__.py +0 -0
  18. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/api/_otel_bootstrap.py +0 -0
  19. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/api/fixtures/tour_demo_deck.json +0 -0
  20. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/api/main.py +0 -0
  21. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/api/mcp_auth.py +0 -0
  22. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/api/middleware/__init__.py +0 -0
  23. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/api/middleware/csrf.py +0 -0
  24. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/api/middleware/request_logging.py +0 -0
  25. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/api/middleware/security_headers.py +0 -0
  26. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/api/routes/__init__.py +0 -0
  27. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/api/routes/_authz.py +0 -0
  28. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/api/routes/admin.py +0 -0
  29. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/api/routes/admin_usage.py +0 -0
  30. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/api/routes/agent_config.py +0 -0
  31. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/api/routes/chat.py +0 -0
  32. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/api/routes/deck_contributors.py +0 -0
  33. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/api/routes/feedback.py +0 -0
  34. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/api/routes/google_slides.py +0 -0
  35. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/api/routes/images.py +0 -0
  36. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/api/routes/local_version.py +0 -0
  37. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/api/routes/profiles.py +0 -0
  38. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/api/routes/sessions.py +0 -0
  39. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/api/routes/settings/__init__.py +0 -0
  40. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/api/routes/settings/contributors.py +0 -0
  41. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/api/routes/settings/deck_prompts.py +0 -0
  42. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/api/routes/settings/design_systems.py +0 -0
  43. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/api/routes/settings/identities.py +0 -0
  44. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/api/routes/settings/slide_styles.py +0 -0
  45. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/api/routes/setup.py +0 -0
  46. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/api/routes/slides.py +0 -0
  47. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/api/routes/tools.py +0 -0
  48. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/api/routes/tour.py +0 -0
  49. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/api/routes/verification.py +0 -0
  50. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/api/routes/version.py +0 -0
  51. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/api/schemas/__init__.py +0 -0
  52. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/api/schemas/agent_config.py +0 -0
  53. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/api/schemas/feedback.py +0 -0
  54. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/api/schemas/requests.py +0 -0
  55. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/api/schemas/responses.py +0 -0
  56. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/api/schemas/settings/__init__.py +0 -0
  57. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/api/schemas/settings/requests.py +0 -0
  58. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/api/schemas/settings/responses.py +0 -0
  59. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/api/schemas/streaming.py +0 -0
  60. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/api/services/__init__.py +0 -0
  61. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/api/services/chat_service.py +0 -0
  62. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/api/services/export_job_queue.py +0 -0
  63. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/api/services/feedback_service.py +0 -0
  64. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/api/services/job_queue.py +0 -0
  65. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/api/services/session_manager.py +0 -0
  66. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/api/services/session_naming.py +0 -0
  67. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/api/services/usage_events.py +0 -0
  68. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/api/services/usage_service.py +0 -0
  69. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/api/utils/__init__.py +0 -0
  70. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/api/utils/validation.py +0 -0
  71. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/core/__init__.py +0 -0
  72. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/core/config_loader.py +0 -0
  73. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/core/context_utils.py +0 -0
  74. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/core/database.py +0 -0
  75. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/core/databricks_client.py +0 -0
  76. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/core/defaults.py +0 -0
  77. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/core/encryption.py +0 -0
  78. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/core/init_default_profile.py +0 -0
  79. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/core/lakebase.py +0 -0
  80. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/core/migrate_profiles_to_agent_config.py +0 -0
  81. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/core/mlflow_agent_spans.py +0 -0
  82. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/core/mlflow_tracing.py +0 -0
  83. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/core/otel_logging.py +0 -0
  84. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/core/permission_context.py +0 -0
  85. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/core/prompt_modules.py +0 -0
  86. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/core/settings_db.py +0 -0
  87. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/core/user_context.py +0 -0
  88. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/database/__init__.py +0 -0
  89. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/database/models/__init__.py +0 -0
  90. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/database/models/deck_contributor.py +0 -0
  91. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/database/models/design_system.py +0 -0
  92. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/database/models/encryption_key.py +0 -0
  93. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/database/models/feedback.py +0 -0
  94. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/database/models/genie_space.py +0 -0
  95. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/database/models/google_global_credentials.py +0 -0
  96. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/database/models/google_oauth_token.py +0 -0
  97. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/database/models/identity.py +0 -0
  98. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/database/models/image.py +0 -0
  99. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/database/models/profile.py +0 -0
  100. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/database/models/profile_contributor.py +0 -0
  101. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/database/models/prompts.py +0 -0
  102. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/database/models/request_log.py +0 -0
  103. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/database/models/session.py +0 -0
  104. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/database/models/slide_deck_prompt.py +0 -0
  105. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/database/models/slide_style_library.py +0 -0
  106. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/database/models/usage_event.py +0 -0
  107. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/database/models/user_preference.py +0 -0
  108. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/database/types.py +0 -0
  109. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/domain/__init__.py +0 -0
  110. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/domain/slide.py +0 -0
  111. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/domain/slide_deck.py +0 -0
  112. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/services/__init__.py +0 -0
  113. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/services/agent.py +0 -0
  114. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/services/agent_factory.py +0 -0
  115. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/services/config_service.py +0 -0
  116. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/services/config_validator.py +0 -0
  117. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/services/converter_jail/__init__.py +0 -0
  118. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/services/converter_jail/ast_guard.py +0 -0
  119. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/services/converter_jail/codeprep.py +0 -0
  120. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/services/converter_jail/gslides_runner.py +0 -0
  121. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/services/converter_jail/jail.py +0 -0
  122. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/services/converter_jail/pptx_runner.py +0 -0
  123. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/services/converter_jail/protocol.py +0 -0
  124. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/services/design_system_service.py +0 -0
  125. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/services/design_system_templates.py +0 -0
  126. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/services/drive_uploader.py +0 -0
  127. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/services/evaluation/__init__.py +0 -0
  128. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/services/evaluation/llm_judge.py +0 -0
  129. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/services/genie_service.py +0 -0
  130. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/services/google_slides_auth.py +0 -0
  131. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/services/google_slides_prompts_defaults.py +0 -0
  132. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/services/html_to_google_slides.py +0 -0
  133. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/services/html_to_pptx.py +0 -0
  134. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/services/identity_provider.py +0 -0
  135. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/services/identity_providers/__init__.py +0 -0
  136. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/services/identity_providers/account_provider.py +0 -0
  137. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/services/identity_providers/local_provider.py +0 -0
  138. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/services/identity_providers/workspace_provider.py +0 -0
  139. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/services/image_service.py +0 -0
  140. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/services/image_tools.py +0 -0
  141. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/services/permission_service.py +0 -0
  142. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/services/pptx_from_html_huashu.py +0 -0
  143. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/services/pptx_from_records.py +0 -0
  144. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/services/pptx_prompts_defaults.py +0 -0
  145. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/services/profile_service.py +0 -0
  146. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/services/streaming_callback.py +0 -0
  147. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/services/tools/__init__.py +0 -0
  148. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/services/tools/agent_bricks_tool.py +0 -0
  149. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/services/tools/ds_asset_tool.py +0 -0
  150. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/services/tools/genie_tool.py +0 -0
  151. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/services/tools/mcp_tool.py +0 -0
  152. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/services/tools/model_endpoint_tool.py +0 -0
  153. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/services/tools/vector_tool.py +0 -0
  154. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/services/validator.py +0 -0
  155. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/utils/__init__.py +0 -0
  156. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/utils/css_utils.py +0 -0
  157. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/utils/ds_asset_utils.py +0 -0
  158. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/utils/error_handling.py +0 -0
  159. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/utils/html_safety.py +0 -0
  160. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/utils/html_utils.py +0 -0
  161. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/utils/image_utils.py +0 -0
  162. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/utils/js_validator.py +0 -0
  163. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/utils/logging_config.py +0 -0
  164. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/utils/pi_filter.py +0 -0
  165. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/utils/slide_hash.py +0 -0
  166. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/utils/spotlight.py +0 -0
  167. {databricks_tellr_app-0.4.2.dev14 → databricks_tellr_app-0.4.2.dev16}/src/utils/text_caps.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: databricks-tellr-app
3
- Version: 0.4.2.dev14
3
+ Version: 0.4.2.dev16
4
4
  Summary: Tellr application package for Databricks Apps
5
5
  Requires-Python: >=3.10
6
6
  Description-Content-Type: text/markdown
@@ -33,6 +33,7 @@ Requires-Dist: pandas==2.3.3
33
33
  Requires-Dist: python-pptx==0.6.23
34
34
  Requires-Dist: playwright==1.48.0
35
35
  Requires-Dist: Pillow==11.3.0
36
+ Requires-Dist: svgpathtools>=1.6.0
36
37
  Requires-Dist: google-api-python-client==2.190.0
37
38
  Requires-Dist: google-auth-oauthlib==1.2.4
38
39
  Requires-Dist: google-auth-httplib2==0.3.0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: databricks-tellr-app
3
- Version: 0.4.2.dev14
3
+ Version: 0.4.2.dev16
4
4
  Summary: Tellr application package for Databricks Apps
5
5
  Requires-Python: >=3.10
6
6
  Description-Content-Type: text/markdown
@@ -33,6 +33,7 @@ Requires-Dist: pandas==2.3.3
33
33
  Requires-Dist: python-pptx==0.6.23
34
34
  Requires-Dist: playwright==1.48.0
35
35
  Requires-Dist: Pillow==11.3.0
36
+ Requires-Dist: svgpathtools>=1.6.0
36
37
  Requires-Dist: google-api-python-client==2.190.0
37
38
  Requires-Dist: google-auth-oauthlib==1.2.4
38
39
  Requires-Dist: google-auth-httplib2==0.3.0
@@ -27,6 +27,7 @@ pandas==2.3.3
27
27
  python-pptx==0.6.23
28
28
  playwright==1.48.0
29
29
  Pillow==11.3.0
30
+ svgpathtools>=1.6.0
30
31
  google-api-python-client==2.190.0
31
32
  google-auth-oauthlib==1.2.4
32
33
  google-auth-httplib2==0.3.0
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "databricks-tellr-app"
7
- version = "0.4.2.dev14"
7
+ version = "0.4.2.dev16"
8
8
  description = "Tellr application package for Databricks Apps"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -38,6 +38,19 @@ dependencies = [
38
38
  "python-pptx==0.6.23",
39
39
  "playwright==1.48.0",
40
40
  "Pillow==11.3.0",
41
+ # WF-02: imported by BOTH Python emitters (html_to_pptx, html_to_google_slides)
42
+ # and declared in the root manifest, but never here — so on every deployment the
43
+ # lazy `from svgpathtools import parse_path` failed and image extraction became a
44
+ # silent no-op, letting base64 survive into the prompt.
45
+ #
46
+ # Ranged, matching the root declaration, rather than exact-pinned like the
47
+ # resolver-speedup block below. MEASURED before shipping, because a new
48
+ # dependency here is what has previously blown the Apps build budget: adding it
49
+ # takes the resolution from 196 to 198 packages in 15.7s vs 18.5s baseline — no
50
+ # backtracking, and FASTER, not slower. Its numpy and scipy requirements are
51
+ # already in the closure (via mlflow / scikit-learn / skops / pandas), so the only
52
+ # new distributions are svgpathtools itself and the tiny pure-Python svgwrite.
53
+ "svgpathtools>=1.6.0",
41
54
  "google-api-python-client==2.190.0",
42
55
  "google-auth-oauthlib==1.2.4",
43
56
  "google-auth-httplib2==0.3.0",
@@ -649,7 +649,10 @@ def _render_deck_response(
649
649
  "Poll the status of a deck generation or edit job. Returns "
650
650
  "lightweight status while pending/running; when ready, returns the "
651
651
  "complete deck as structured slide data, a standalone HTML "
652
- "document, and URLs into tellr's full editor and view-only surfaces."
652
+ "document, and URLs into tellr's full editor and view-only surfaces. "
653
+ "A ready turn is not always an applied change: when "
654
+ "metadata.clarification_needed is true the deck was left UNCHANGED and "
655
+ "metadata.clarification carries the question to answer before retrying."
653
656
  ),
654
657
  )
655
658
  async def get_deck_status(
@@ -795,6 +798,34 @@ async def _get_deck_status_impl(
795
798
  for m in db_messages
796
799
  ]
797
800
 
801
+ # WG2-01. An edit instruction that names no slide is DELIBERATELY not
802
+ # applied: chat_service asks which slide instead (its RC10 twins, plus the
803
+ # ambiguous add-vs-replace pair) and attaches ``clarification_needed`` to
804
+ # the COMPLETE event, which ``job_queue.process_chat_request`` copies
805
+ # verbatim into ``result["metadata"]``.
806
+ #
807
+ # Such a turn completes normally, so "ready" is the honest status and
808
+ # ``replacement_info`` is None — which is indistinguishable from an edit
809
+ # that applied but replaced nothing. Rebuilding ``metadata`` from
810
+ # hand-picked keys DROPPED the flag, so an automated caller reading
811
+ # ``status`` concluded the edit had landed when the deck was untouched; the
812
+ # only remaining trace was a ``message_type == "clarification"`` row in the
813
+ # transcript. Surfaced ADDITIVELY, and ``status`` keeps its existing meaning
814
+ # for clients already reading it.
815
+ clarification_needed = bool(result_metadata.get("clarification_needed"))
816
+ clarification = None
817
+ if clarification_needed:
818
+ # The question itself, so a caller can act on it without re-filtering
819
+ # the transcript. Last one wins: it is the turn's most recent ask.
820
+ clarification = next(
821
+ (
822
+ message.get("content")
823
+ for message in reversed(messages)
824
+ if message.get("message_type") == "clarification"
825
+ ),
826
+ None,
827
+ )
828
+
798
829
  return {
799
830
  "session_id": session_id,
800
831
  "request_id": request_id,
@@ -808,6 +839,8 @@ async def _get_deck_status_impl(
808
839
  or result_metadata.get("latency_seconds"),
809
840
  "experiment_url": result.get("experiment_url"),
810
841
  "session_title": result.get("session_title"),
842
+ "clarification_needed": clarification_needed,
843
+ "clarification": clarification,
811
844
  },
812
845
  }
813
846
  except MCPToolError:
@@ -851,7 +884,11 @@ def _check_contiguous(indices: list[int]) -> None:
851
884
  "The edit is applied in-place; the session_id and deck_url stay "
852
885
  "stable across edits. Returns a request_id; the caller polls "
853
886
  "get_deck_status for completion and receives the updated deck "
854
- "plus replacement_info summarizing what changed."
887
+ "plus replacement_info summarizing what changed. An instruction that "
888
+ "identifies no slide — neither via slide_indices nor by naming one in "
889
+ "the text — is answered with a clarifying question INSTEAD of an edit: "
890
+ "get_deck_status then reports metadata.clarification_needed with the "
891
+ "deck unchanged."
855
892
  ),
856
893
  )
857
894
  async def edit_deck(
@@ -1,6 +1,7 @@
1
1
  """Export endpoints for PDF and PPTX generation."""
2
2
 
3
3
  import logging
4
+ import re
4
5
  import tempfile
5
6
  from pathlib import Path
6
7
  from typing import Optional
@@ -41,6 +42,33 @@ class ExportPPTXRequest(BaseModel):
41
42
  chart_images: Optional[list[list[ChartImage]]] = None # Chart images per slide (client-side captured)
42
43
 
43
44
 
45
+ # WF-03. `@import` is only valid before every other rule of ITS OWN STYLESHEET, and this
46
+ # document used to inject `*{}` and `html,body{}` ahead of the deck's CSS in a SINGLE
47
+ # `<style>` — so a deck whose CSS OPENS with a webfont `@import` had that rule silently
48
+ # discarded. Measured: 0 font faces in the export against 35 in the UI, which was the whole
49
+ # of the long-unexplained 38.25 px per-component residual on the DS-OFF deck.
50
+ #
51
+ # The fix is STRUCTURAL, and it is the reason there is no CSS parsing here. Two `<style>`
52
+ # elements are two stylesheets, so the deck gets its OWN `<style>`: its leading `@import` is
53
+ # then already first in its own sheet and valid with NO rewriting at all. Cascade order
54
+ # between separate `<style>` elements is document order, so resets-then-deck-then-resets
55
+ # keeps exactly the precedence the single combined sheet had.
56
+ #
57
+ # MEASURED IN CHROMIUM under the real SLIDE_CSP: combined -> 1 sheet, 0 font faces, import
58
+ # dead; split -> 2 sheets, font faces registered, import applied; and with a reset and the
59
+ # deck setting the same property at equal specificity, the DECK still wins on order.
60
+ #
61
+ # This REPLACES a hand-written leading-`@charset`/`@import` scanner that lived here. Three
62
+ # rounds of review found four tokenizer defects in it (`str.isspace()` accepting NBSP, an
63
+ # unescaped top-level `\)`, a lone `@charset` rewriting the document, an ASCII-only ident
64
+ # guard) and then a fifth (`@layer name;` may legally precede `@import`, which it missed,
65
+ # re-losing the font for ordinary modern CSS). Every one of those was a bug in DECIDING what
66
+ # the deck's CSS means. Emitting the deck's CSS VERBATIM in its own sheet removes the need to
67
+ # decide, so that entire class of defect is now unreachable rather than patched: NUL bytes,
68
+ # top-level `<!--`, a lone CR inside a string, escaped at-keywords and `@layer` are all
69
+ # simply the browser's business, exactly as they are in the deck's own stylesheet.
70
+
71
+
44
72
  def build_slide_html(slide: dict, slide_deck: dict) -> str:
45
73
  """Build complete HTML for a single slide.
46
74
 
@@ -56,12 +84,11 @@ def build_slide_html(slide: dict, slide_deck: dict) -> str:
56
84
  external_scripts = slide_deck.get("external_scripts", [])
57
85
  deck_css = slide_deck.get("css", "")
58
86
  deck_scripts = slide_deck.get("scripts", "")
59
-
87
+
60
88
  # Clean up deck_scripts: remove any trailing extra IIFE closings
61
89
  # deck_scripts should be a series of (function() { ... })(); blocks
62
90
  # but sometimes there might be an extra })(); at the end
63
91
  if deck_scripts:
64
- import re
65
92
  # Remove any trailing })(); that might be extra
66
93
  deck_scripts = deck_scripts.rstrip()
67
94
  # Count opening and closing IIFEs
@@ -138,6 +165,28 @@ def build_slide_html(slide: dict, slide_deck: dict) -> str:
138
165
  extra={"slide_id": slide_id, "patterns": findings},
139
166
  )
140
167
 
168
+ # THE FIRST RESET SHEET BELOW IS SCOPED TO `html, body`, NEVER UNIVERSAL.
169
+ #
170
+ # A universal reset — border-box plus zeroed margin and padding, on every
171
+ # element — used to sit there. It laid slide DESCENDANTS out border-box and
172
+ # margin-free in the export while every preview surface lays them out
173
+ # content-box with UA margins, which is the box model Claude Design ground
174
+ # truth uses. That cost up to 69.87 px of per-component drift here, on the
175
+ # same 6 live slides the standalone builder diverged on by the same mechanism.
176
+ #
177
+ # This shell needs no box model of its own: it is a fixed 1280x720 document
178
+ # whose padding is zeroed both above and (after deck CSS) below, so the two
179
+ # box models are provably identical for html/body here. The standalone
180
+ # MULTI-slide builder does pad `body`, so it keeps `box-sizing` on its shell —
181
+ # see buildStandaloneDeckDocument in frontend/src/services/slideDocument.ts.
182
+ #
183
+ # Deliberately a `#` comment and NOT part of the f-string. Anything inside the
184
+ # f-string is EMITTED into every exported document, where it (a) displaces
185
+ # useful deck CSS from the ~15,000-char budget `_truncate_html` hands the LLM,
186
+ # and (b) eats headroom under the 2000-char length threshold a sheet-role
187
+ # classifier uses to tell this reset sheet from the deck's own. Keep the
188
+ # rendered comment to one short line and put the reasoning here instead.
189
+ # Pinned by tests/unit/test_preview_box_model_parity.py.
141
190
  complete_html = f"""<!DOCTYPE html>
142
191
  <html lang="en">
143
192
  <head>
@@ -147,11 +196,7 @@ def build_slide_html(slide: dict, slide_deck: dict) -> str:
147
196
  <title>{slide_deck.get("title", "Slide")} - Slide {slide.get("slide_id", "")}</title>
148
197
  {scripts_html}
149
198
  <style>
150
- * {{
151
- box-sizing: border-box;
152
- margin: 0;
153
- padding: 0;
154
- }}
199
+ /* Shell-scoped, never universal: slide content stays content-box, as in every preview. */
155
200
  html, body {{
156
201
  width: 1280px;
157
202
  height: 720px;
@@ -161,7 +206,9 @@ def build_slide_html(slide: dict, slide_deck: dict) -> str:
161
206
  background: #ffffff;
162
207
  font-family: 'Inter', 'Segoe UI', system-ui, -apple-system, sans-serif;
163
208
  }}
164
- {deck_css}
209
+ </style>
210
+ <style>{deck_css}</style>
211
+ <style>
165
212
  /* After deck CSS: the fixed-frame document owns its OWN box (WM-01).
166
213
  The html/body rule above is emitted BEFORE the deck, so a deck-authored
167
214
  `body {{ padding: 48px 0; gap: 48px }}` — real generator output — used to win
@@ -178,7 +225,10 @@ def build_slide_html(slide: dict, slide_deck: dict) -> str:
178
225
  depends on `body {{ padding: 40px 20px }}`.
179
226
  No `!important`, matching the frontend rule: it wins by ORDER at equal
180
227
  specificity, so both surfaces treat a deck's `!important` body padding the
181
- same way and cannot drift apart. */
228
+ same way and cannot drift apart. That ORDER now spans separate `<style>`
229
+ elements — the deck has a stylesheet of its own so its leading `@import`
230
+ stays valid (WF-03) — and the cascade orders separate sheets by DOCUMENT
231
+ ORDER, so this later sheet still wins exactly as it did inside one block. */
182
232
  html, body {{
183
233
  margin: 0;
184
234
  padding: 0;
@@ -258,7 +258,27 @@ _STYLE_HEADER = "SLIDE VISUAL STYLE"
258
258
  # BLAST RADIUS, measured: only systems whose largest sub-body rung equals the floor
259
259
  # compile differently. Every other ramp shape — including the one the docstring above
260
260
  # describes — is byte-identical apart from this version marker.
261
- COMPILER_VERSION = 19
261
+ # v20: WE-02's imperative contrast-pairing wording is REVERTED for failing its
262
+ # measurement gate, so persisted v19 rows hold ~500 B of prose this compiler no
263
+ # longer emits and the bump is what makes them recompile.
264
+ #
265
+ # THE BUMP IS LOAD-BEARING HERE, unlike the edit it undoes. When WE-02 shipped it
266
+ # rode the v19 bump on the reasoning that "v19 is unreleased, so v18 rows are
267
+ # invalidated once, not twice" — and that was true AT THE TIME. It is not true now:
268
+ # v19 HAS been released and deployed, and rows compiled by it exist carrying the
269
+ # imperative wording. Currency is an exact, version-stamped sentinel match at the
270
+ # front of the artifact (:data:`_CURRENCY_SENTINEL`,
271
+ # :func:`compiled_style_content_is_current`), so a stored v19 artifact would keep
272
+ # reading CURRENT under a v19 compiler and would NEVER be recompiled — the revert
273
+ # would ship while every existing row went on serving the reverted text. Changing
274
+ # what a RELEASED version emits without moving the stamp is precisely the failure
275
+ # mode the sentinel exists to prevent.
276
+ #
277
+ # BLAST RADIUS: every design system recompiles once, and the only difference is the
278
+ # removal of the imperative pairing prose (plus the version marker itself). WL-02's
279
+ # safe-area wording, which PASSED its gate in the same commit, is deliberately
280
+ # untouched and stays exactly as v19 emitted it.
281
+ COMPILER_VERSION = 20
262
282
  _COMPILER_VERSION_MARKER = f"[ds-compiler v{COMPILER_VERSION}]"
263
283
 
264
284
  # The EXACT, name-independent header prefix every compiled artifact opens with.
@@ -1599,29 +1619,54 @@ _CONTRAST_DERIVED_HEADING = (
1599
1619
  # extremes do not: the measured failures were mid-tone ON mid-tone, which is
1600
1620
  # exactly the pair a palette makes easy to reach for and a luminance table cannot
1601
1621
  # enumerate without listing every combination.
1602
- # MEASUREMENT-GATED (WE-02). The pairings above were AVAILABLE AND IGNORED, not
1603
- # missing: on the measured deck all three offending backgrounds were enumerated WITH
1604
- # a prescribed AA-passing text color, and the model chose otherwise every time —
1605
- # coral #FF5F46 on oat #F9F7F4 at 2.8146:1 where #000000 (19.64:1) was named, lava
1606
- # #FF3621 on white where #000000 (21:1) was named, and for the CTA it picked the
1607
- # EXACT INVERSE of what the artifact stated for that background. 24 of 108 rated
1608
- # node-cells fell below 4.5:1; 0 were primary text, and every offender was a brand
1609
- # ACCENT used as text, all of them in the model-authored CSS tail. No color was
1610
- # invented — the palette was misused, not exceeded.
1611
- #
1612
- # So the pairing is stated as a REQUIREMENT for the TEXT role specifically. The
1613
- # colors themselves are NOT rewritten: silently correcting a brand's palette would
1614
- # defeat the purpose of a design system.
1615
- _CONTRAST_TEXT_ROLE_IMPERATIVE_LINE = (
1616
- "- Those pairings are REQUIREMENTS for TEXT, not suggestions: whenever you use a "
1617
- "background listed above, its text color is the one named for it. Do NOT use a "
1618
- "brand accent (or any other palette color) as the text color on a listed "
1619
- "background because it looks on-brand — accents belong in fills, rules, icons "
1620
- "and imagery unless that exact pair meets the ratio above. This applies to small "
1621
- "label text (eyebrows, kickers, captions, tags) and to link and button labels "
1622
- "just as much as to body copy."
1623
- )
1624
-
1622
+ # WE-02 ONCE ADDED MORE HERE, AND IT WAS REVERTED FOR FAILING ITS MEASUREMENT GATE.
1623
+ # Recorded so the same lever is not reached for again without new evidence.
1624
+ #
1625
+ # The diagnosis was and remains correct: the pairings are AVAILABLE AND IGNORED, not
1626
+ # missing. On the deck that prompted it, all three offending backgrounds were
1627
+ # enumerated WITH a prescribed AA-passing text color and the model chose otherwise
1628
+ # every time — coral #FF5F46 on oat #F9F7F4 at 2.8146:1 where #000000 (19.64:1) was
1629
+ # named, and for the CTA the EXACT INVERSE of what the artifact stated. No color was
1630
+ # invented; the palette was misused.
1631
+ #
1632
+ # The attempted fix restated the pairing as a REQUIREMENT for the TEXT role, plus a
1633
+ # line forbidding a brand accent as text on a listed background and naming small
1634
+ # label text and button labels explicitly. It was shipped conditional on measurement.
1635
+ #
1636
+ # THE MEASUREMENT (re-run at n=4: 13 slides, 660 rated node-cells, painted-pixel
1637
+ # backdrops, Python recompute agreeing 660/660) found it INERT. Against the
1638
+ # byte-identical dev13 prompt the failures reproduce EXACTLY — 12 sub-threshold
1639
+ # node-cells on BOTH builds, the same 3 nodes, the same two pairs, the same ratios
1640
+ # (coral eyebrow 2.8146 twice, CTA #FFFFFF on lava #FF3621 3.6197). Across n=4:
1641
+ # 208/660 below 4.5, 156/660 below their own threshold, the eyebrow pair failing on
1642
+ # 4/4 decks, the CTA inverse on 2/2 decks that produced a button, and the prescribed
1643
+ # ink ignored on 552/624 enumerated-background cells. ~500 B of prompt, no behaviour
1644
+ # change. Inert, not harmful — and inert prose is still cost, so it goes.
1645
+ #
1646
+ # WHAT THIS MEANS FOR THE NEXT ATTEMPT: restating the POSITIVE pairing more forcefully
1647
+ # is not the lever. Two candidates remain, in the order the re-run ranks them, and
1648
+ # NEITHER is implemented here:
1649
+ #
1650
+ # 1. WE-03, the reverse-direction NEGATIVE list — "do not use <color> as text" —
1651
+ # which is still untested. The reason it is the cheaper next step is structural:
1652
+ # every offending color is one the artifact never names as unsafe AS TEXT. The
1653
+ # `unusable` line below is legitimately EMPTY for this brand (its extremes are
1654
+ # pure #FFFFFF/#000000, so every resolvable color token reaches AA with one of
1655
+ # them), so the one line that would have warned never fires. A negative list
1656
+ # keyed on the TEXT role, not on unusability against both extremes, is a
1657
+ # different statement from anything the artifact makes today.
1658
+ # 2. POST-GENERATION VALIDATION of rendered text/background pairs, which is the only
1659
+ # remedy that does not depend on the model complying with prose at all.
1660
+ #
1661
+ # The re-run also found 12 unique PRIMARY-text nodes below 4.5 on prompts never
1662
+ # previously exercised (subtitle #618794 on navy 3.4961, body grey #8A8580 on white
1663
+ # 3.6538) — new coverage rather than proven degradation, but it shows the model can
1664
+ # still choose sub-threshold PRIMARY text, which is what makes (2) eventually
1665
+ # necessary. Both recorded as known-open.
1666
+ #
1667
+ # The declarative pairing itself is v18 content that closed the contrast vacuum and
1668
+ # is untouched by the revert. Colors are never rewritten either way: silently
1669
+ # correcting a brand's palette would defeat the purpose of a design system.
1625
1670
  _CONTRAST_PAIR_RULE_LINE = (
1626
1671
  "- For any other pair of these colors, work out the ratio before you use it: "
1627
1672
  f"two mid-tone brand colors together almost never reach {_CONTRAST_AA_NORMAL:g}:1. "
@@ -1821,16 +1866,14 @@ def _contrast_section(grouped: dict[str, list[tuple[str, str]]]) -> str:
1821
1866
  ]
1822
1867
  if on_lightest:
1823
1868
  lines.append(
1824
- f"- On these backgrounds the text color MUST be {lightest}: "
1869
+ f"- Use {lightest} as the text color on these backgrounds: "
1825
1870
  f"{', '.join(on_lightest)}"
1826
1871
  )
1827
1872
  if on_darkest:
1828
1873
  lines.append(
1829
- f"- On these backgrounds the text color MUST be {darkest}: "
1874
+ f"- Use {darkest} as the text color on these backgrounds: "
1830
1875
  f"{', '.join(on_darkest)}"
1831
1876
  )
1832
- if on_lightest or on_darkest:
1833
- lines.append(_CONTRAST_TEXT_ROLE_IMPERATIVE_LINE)
1834
1877
  if unusable:
1835
1878
  lines.append(
1836
1879
  "- Never set normal text on these colors — they reach "