regstack 0.4.0__tar.gz → 0.5.0__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 (238) hide show
  1. {regstack-0.4.0 → regstack-0.5.0}/CLAUDE.md +14 -0
  2. {regstack-0.4.0 → regstack-0.5.0}/PKG-INFO +5 -5
  3. {regstack-0.4.0 → regstack-0.5.0}/README.md +4 -4
  4. {regstack-0.4.0 → regstack-0.5.0}/docs/changelog.md +27 -0
  5. regstack-0.5.0/docs/cli.md +187 -0
  6. {regstack-0.4.0 → regstack-0.5.0}/docs/configuration.md +8 -7
  7. {regstack-0.4.0 → regstack-0.5.0}/docs/embedding.md +5 -5
  8. {regstack-0.4.0 → regstack-0.5.0}/docs/index.md +53 -2
  9. {regstack-0.4.0 → regstack-0.5.0}/docs/quickstart.md +1 -1
  10. {regstack-0.4.0 → regstack-0.5.0}/docs/theming.md +46 -1
  11. {regstack-0.4.0 → regstack-0.5.0}/pyproject.toml +1 -1
  12. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/cli/__main__.py +15 -0
  13. regstack-0.5.0/src/regstack/version.py +1 -0
  14. regstack-0.5.0/src/regstack/wizard/theme_designer/__init__.py +8 -0
  15. regstack-0.5.0/src/regstack/wizard/theme_designer/cli.py +181 -0
  16. regstack-0.5.0/src/regstack/wizard/theme_designer/routes.py +278 -0
  17. regstack-0.5.0/src/regstack/wizard/theme_designer/server.py +87 -0
  18. regstack-0.5.0/src/regstack/wizard/theme_designer/static/designer.css +348 -0
  19. regstack-0.5.0/src/regstack/wizard/theme_designer/static/designer.js +272 -0
  20. regstack-0.5.0/src/regstack/wizard/theme_designer/templates/designer.html +89 -0
  21. regstack-0.5.0/src/regstack/wizard/theme_designer/validators.py +145 -0
  22. regstack-0.5.0/src/regstack/wizard/theme_designer/window.py +63 -0
  23. regstack-0.5.0/src/regstack/wizard/theme_designer/writer.py +196 -0
  24. regstack-0.5.0/tests/e2e/conftest.py +121 -0
  25. regstack-0.5.0/tests/e2e/test_theme_designer.py +87 -0
  26. regstack-0.5.0/tests/unit/test_cli_migrate.py +151 -0
  27. regstack-0.5.0/tests/unit/test_theme_designer_cli.py +190 -0
  28. regstack-0.5.0/tests/unit/test_theme_designer_routes.py +179 -0
  29. regstack-0.5.0/tests/unit/test_theme_designer_validators.py +154 -0
  30. regstack-0.5.0/tests/unit/test_theme_designer_writer.py +156 -0
  31. regstack-0.5.0/tests/unit/test_wizard_oauth_cli.py +230 -0
  32. {regstack-0.4.0 → regstack-0.5.0}/uv.lock +1 -1
  33. regstack-0.4.0/docs/cli.md +0 -87
  34. regstack-0.4.0/src/regstack/version.py +0 -1
  35. regstack-0.4.0/tests/e2e/conftest.py +0 -82
  36. regstack-0.4.0/tests/unit/test_wizard_oauth_cli.py +0 -93
  37. {regstack-0.4.0 → regstack-0.5.0}/.github/workflows/publish.yml +0 -0
  38. {regstack-0.4.0 → regstack-0.5.0}/.github/workflows/test.yml +0 -0
  39. {regstack-0.4.0 → regstack-0.5.0}/.gitignore +0 -0
  40. {regstack-0.4.0 → regstack-0.5.0}/.python-version +0 -0
  41. {regstack-0.4.0 → regstack-0.5.0}/.readthedocs.yaml +0 -0
  42. {regstack-0.4.0 → regstack-0.5.0}/CHANGELOG.md +0 -0
  43. {regstack-0.4.0 → regstack-0.5.0}/LICENSE +0 -0
  44. {regstack-0.4.0 → regstack-0.5.0}/NOTICE +0 -0
  45. {regstack-0.4.0 → regstack-0.5.0}/SECURITY.md +0 -0
  46. {regstack-0.4.0 → regstack-0.5.0}/docs/_static/.gitkeep +0 -0
  47. {regstack-0.4.0 → regstack-0.5.0}/docs/_templates/.gitkeep +0 -0
  48. {regstack-0.4.0 → regstack-0.5.0}/docs/api.md +0 -0
  49. {regstack-0.4.0 → regstack-0.5.0}/docs/architecture.md +0 -0
  50. {regstack-0.4.0 → regstack-0.5.0}/docs/conf.py +0 -0
  51. {regstack-0.4.0 → regstack-0.5.0}/docs/oauth.md +0 -0
  52. {regstack-0.4.0 → regstack-0.5.0}/docs/security-reports/README.md +0 -0
  53. {regstack-0.4.0 → regstack-0.5.0}/docs/security.md +0 -0
  54. {regstack-0.4.0 → regstack-0.5.0}/examples/_common/__init__.py +0 -0
  55. {regstack-0.4.0 → regstack-0.5.0}/examples/_common/app.py +0 -0
  56. {regstack-0.4.0 → regstack-0.5.0}/examples/mongo/README.md +0 -0
  57. {regstack-0.4.0 → regstack-0.5.0}/examples/mongo/branding/theme.css +0 -0
  58. {regstack-0.4.0 → regstack-0.5.0}/examples/mongo/main.py +0 -0
  59. {regstack-0.4.0 → regstack-0.5.0}/examples/mongo/regstack.toml +0 -0
  60. {regstack-0.4.0 → regstack-0.5.0}/examples/postgres/README.md +0 -0
  61. {regstack-0.4.0 → regstack-0.5.0}/examples/postgres/main.py +0 -0
  62. {regstack-0.4.0 → regstack-0.5.0}/examples/postgres/regstack.toml +0 -0
  63. {regstack-0.4.0 → regstack-0.5.0}/examples/sqlite/README.md +0 -0
  64. {regstack-0.4.0 → regstack-0.5.0}/examples/sqlite/main.py +0 -0
  65. {regstack-0.4.0 → regstack-0.5.0}/examples/sqlite/regstack.toml +0 -0
  66. {regstack-0.4.0 → regstack-0.5.0}/regstack.toml.example +0 -0
  67. {regstack-0.4.0 → regstack-0.5.0}/scripts/security-review-prompt.md +0 -0
  68. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/__init__.py +0 -0
  69. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/app.py +0 -0
  70. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/auth/__init__.py +0 -0
  71. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/auth/clock.py +0 -0
  72. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/auth/dependencies.py +0 -0
  73. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/auth/jwt.py +0 -0
  74. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/auth/lockout.py +0 -0
  75. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/auth/mfa.py +0 -0
  76. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/auth/password.py +0 -0
  77. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/auth/tokens.py +0 -0
  78. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/backends/__init__.py +0 -0
  79. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/backends/base.py +0 -0
  80. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/backends/factory.py +0 -0
  81. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/backends/mongo/__init__.py +0 -0
  82. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/backends/mongo/backend.py +0 -0
  83. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/backends/mongo/client.py +0 -0
  84. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/backends/mongo/indexes.py +0 -0
  85. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/backends/mongo/repositories/__init__.py +0 -0
  86. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/backends/mongo/repositories/blacklist_repo.py +0 -0
  87. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/backends/mongo/repositories/login_attempt_repo.py +0 -0
  88. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/backends/mongo/repositories/mfa_code_repo.py +0 -0
  89. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/backends/mongo/repositories/oauth_identity_repo.py +0 -0
  90. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/backends/mongo/repositories/oauth_state_repo.py +0 -0
  91. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/backends/mongo/repositories/pending_repo.py +0 -0
  92. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/backends/mongo/repositories/user_repo.py +0 -0
  93. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/backends/protocols.py +0 -0
  94. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/backends/sql/__init__.py +0 -0
  95. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/backends/sql/backend.py +0 -0
  96. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/backends/sql/migrations/__init__.py +0 -0
  97. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/backends/sql/migrations/env.py +0 -0
  98. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/backends/sql/migrations/script.py.mako +0 -0
  99. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/backends/sql/migrations/versions/0001_initial_schema.py +0 -0
  100. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/backends/sql/migrations/versions/0002_oauth.py +0 -0
  101. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/backends/sql/repositories/__init__.py +0 -0
  102. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/backends/sql/repositories/blacklist_repo.py +0 -0
  103. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/backends/sql/repositories/login_attempt_repo.py +0 -0
  104. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/backends/sql/repositories/mfa_code_repo.py +0 -0
  105. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/backends/sql/repositories/oauth_identity_repo.py +0 -0
  106. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/backends/sql/repositories/oauth_state_repo.py +0 -0
  107. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/backends/sql/repositories/pending_repo.py +0 -0
  108. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/backends/sql/repositories/user_repo.py +0 -0
  109. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/backends/sql/schema.py +0 -0
  110. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/backends/sql/types.py +0 -0
  111. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/cli/__init__.py +0 -0
  112. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/cli/_runtime.py +0 -0
  113. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/cli/admin.py +0 -0
  114. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/cli/doctor.py +0 -0
  115. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/cli/init.py +0 -0
  116. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/cli/migrate.py +0 -0
  117. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/config/__init__.py +0 -0
  118. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/config/loader.py +0 -0
  119. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/config/schema.py +0 -0
  120. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/config/secrets.py +0 -0
  121. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/email/__init__.py +0 -0
  122. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/email/base.py +0 -0
  123. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/email/composer.py +0 -0
  124. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/email/console.py +0 -0
  125. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/email/factory.py +0 -0
  126. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/email/ses.py +0 -0
  127. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/email/smtp.py +0 -0
  128. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/email/templates/email_change.html +0 -0
  129. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/email/templates/email_change.subject.txt +0 -0
  130. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/email/templates/email_change.txt +0 -0
  131. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/email/templates/password_reset.html +0 -0
  132. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/email/templates/password_reset.subject.txt +0 -0
  133. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/email/templates/password_reset.txt +0 -0
  134. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/email/templates/sms_login_mfa.txt +0 -0
  135. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/email/templates/sms_phone_setup.txt +0 -0
  136. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/email/templates/verification.html +0 -0
  137. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/email/templates/verification.subject.txt +0 -0
  138. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/email/templates/verification.txt +0 -0
  139. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/hooks/__init__.py +0 -0
  140. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/hooks/events.py +0 -0
  141. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/models/__init__.py +0 -0
  142. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/models/_objectid.py +0 -0
  143. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/models/login_attempt.py +0 -0
  144. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/models/mfa_code.py +0 -0
  145. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/models/oauth_identity.py +0 -0
  146. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/models/oauth_state.py +0 -0
  147. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/models/pending_registration.py +0 -0
  148. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/models/user.py +0 -0
  149. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/oauth/__init__.py +0 -0
  150. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/oauth/base.py +0 -0
  151. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/oauth/errors.py +0 -0
  152. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/oauth/providers/__init__.py +0 -0
  153. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/oauth/providers/google.py +0 -0
  154. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/oauth/registry.py +0 -0
  155. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/routers/__init__.py +0 -0
  156. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/routers/_schemas.py +0 -0
  157. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/routers/account.py +0 -0
  158. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/routers/admin.py +0 -0
  159. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/routers/login.py +0 -0
  160. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/routers/logout.py +0 -0
  161. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/routers/oauth.py +0 -0
  162. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/routers/password.py +0 -0
  163. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/routers/phone.py +0 -0
  164. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/routers/register.py +0 -0
  165. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/routers/verify.py +0 -0
  166. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/sms/__init__.py +0 -0
  167. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/sms/base.py +0 -0
  168. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/sms/factory.py +0 -0
  169. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/sms/null.py +0 -0
  170. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/sms/sns.py +0 -0
  171. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/sms/twilio.py +0 -0
  172. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/ui/__init__.py +0 -0
  173. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/ui/pages.py +0 -0
  174. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/ui/static/css/core.css +0 -0
  175. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/ui/static/css/theme.css +0 -0
  176. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/ui/static/js/regstack.js +0 -0
  177. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/ui/templates/auth/email_change_confirm.html +0 -0
  178. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/ui/templates/auth/forgot.html +0 -0
  179. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/ui/templates/auth/login.html +0 -0
  180. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/ui/templates/auth/me.html +0 -0
  181. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/ui/templates/auth/mfa_confirm.html +0 -0
  182. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/ui/templates/auth/oauth_complete.html +0 -0
  183. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/ui/templates/auth/register.html +0 -0
  184. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/ui/templates/auth/reset.html +0 -0
  185. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/ui/templates/auth/verify.html +0 -0
  186. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/ui/templates/base.html +0 -0
  187. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/wizard/__init__.py +0 -0
  188. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/wizard/oauth_google/__init__.py +0 -0
  189. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/wizard/oauth_google/cli.py +0 -0
  190. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/wizard/oauth_google/routes.py +0 -0
  191. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/wizard/oauth_google/server.py +0 -0
  192. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/wizard/oauth_google/static/wizard.css +0 -0
  193. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/wizard/oauth_google/static/wizard.js +0 -0
  194. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/wizard/oauth_google/templates/wizard.html +0 -0
  195. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/wizard/oauth_google/validators.py +0 -0
  196. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/wizard/oauth_google/window.py +0 -0
  197. {regstack-0.4.0 → regstack-0.5.0}/src/regstack/wizard/oauth_google/writer.py +0 -0
  198. {regstack-0.4.0 → regstack-0.5.0}/tasks/oauth-design.md +0 -0
  199. {regstack-0.4.0 → regstack-0.5.0}/tasks.py +0 -0
  200. {regstack-0.4.0 → regstack-0.5.0}/tests/__init__.py +0 -0
  201. {regstack-0.4.0 → regstack-0.5.0}/tests/_fake_google/__init__.py +0 -0
  202. {regstack-0.4.0 → regstack-0.5.0}/tests/_fake_google/provider.py +0 -0
  203. {regstack-0.4.0 → regstack-0.5.0}/tests/conftest.py +0 -0
  204. {regstack-0.4.0 → regstack-0.5.0}/tests/e2e/__init__.py +0 -0
  205. {regstack-0.4.0 → regstack-0.5.0}/tests/e2e/test_wizard_oauth_flow.py +0 -0
  206. {regstack-0.4.0 → regstack-0.5.0}/tests/integration/__init__.py +0 -0
  207. {regstack-0.4.0 → regstack-0.5.0}/tests/integration/test_account_management.py +0 -0
  208. {regstack-0.4.0 → regstack-0.5.0}/tests/integration/test_admin_router.py +0 -0
  209. {regstack-0.4.0 → regstack-0.5.0}/tests/integration/test_happy_path.py +0 -0
  210. {regstack-0.4.0 → regstack-0.5.0}/tests/integration/test_indexes.py +0 -0
  211. {regstack-0.4.0 → regstack-0.5.0}/tests/integration/test_login_lockout.py +0 -0
  212. {regstack-0.4.0 → regstack-0.5.0}/tests/integration/test_mfa.py +0 -0
  213. {regstack-0.4.0 → regstack-0.5.0}/tests/integration/test_oauth_google_router.py +0 -0
  214. {regstack-0.4.0 → regstack-0.5.0}/tests/integration/test_oauth_repos.py +0 -0
  215. {regstack-0.4.0 → regstack-0.5.0}/tests/integration/test_oauth_ui.py +0 -0
  216. {regstack-0.4.0 → regstack-0.5.0}/tests/integration/test_password_reset.py +0 -0
  217. {regstack-0.4.0 → regstack-0.5.0}/tests/integration/test_sql_migrations.py +0 -0
  218. {regstack-0.4.0 → regstack-0.5.0}/tests/integration/test_ui_router.py +0 -0
  219. {regstack-0.4.0 → regstack-0.5.0}/tests/integration/test_verification.py +0 -0
  220. {regstack-0.4.0 → regstack-0.5.0}/tests/unit/__init__.py +0 -0
  221. {regstack-0.4.0 → regstack-0.5.0}/tests/unit/test_base_install_imports.py +0 -0
  222. {regstack-0.4.0 → regstack-0.5.0}/tests/unit/test_cli.py +0 -0
  223. {regstack-0.4.0 → regstack-0.5.0}/tests/unit/test_cli_doctor.py +0 -0
  224. {regstack-0.4.0 → regstack-0.5.0}/tests/unit/test_cli_init.py +0 -0
  225. {regstack-0.4.0 → regstack-0.5.0}/tests/unit/test_config_loader.py +0 -0
  226. {regstack-0.4.0 → regstack-0.5.0}/tests/unit/test_jwt.py +0 -0
  227. {regstack-0.4.0 → regstack-0.5.0}/tests/unit/test_lockout.py +0 -0
  228. {regstack-0.4.0 → regstack-0.5.0}/tests/unit/test_mail_composer.py +0 -0
  229. {regstack-0.4.0 → regstack-0.5.0}/tests/unit/test_mfa_code_repo.py +0 -0
  230. {regstack-0.4.0 → regstack-0.5.0}/tests/unit/test_oauth_google.py +0 -0
  231. {regstack-0.4.0 → regstack-0.5.0}/tests/unit/test_password.py +0 -0
  232. {regstack-0.4.0 → regstack-0.5.0}/tests/unit/test_ses_backend.py +0 -0
  233. {regstack-0.4.0 → regstack-0.5.0}/tests/unit/test_sms.py +0 -0
  234. {regstack-0.4.0 → regstack-0.5.0}/tests/unit/test_smtp_backend.py +0 -0
  235. {regstack-0.4.0 → regstack-0.5.0}/tests/unit/test_ui_env.py +0 -0
  236. {regstack-0.4.0 → regstack-0.5.0}/tests/unit/test_wizard_oauth_routes.py +0 -0
  237. {regstack-0.4.0 → regstack-0.5.0}/tests/unit/test_wizard_oauth_validators.py +0 -0
  238. {regstack-0.4.0 → regstack-0.5.0}/tests/unit/test_wizard_oauth_writer.py +0 -0
@@ -77,6 +77,20 @@ The full plan, including milestone scope and deferred items, lives at
77
77
  four layers: validators (unit), writer (golden-file), routes
78
78
  (TestClient), full SPA flow (Playwright e2e). Run e2e with
79
79
  `inv test-e2e`; `inv test-all` chains it after the backend matrix.
80
+ - **Theme designer — done.** `regstack theme design` opens a sibling
81
+ pywebview window with controls for every `--rs-*` variable and a
82
+ real-time preview of the bundled SSR widgets (renders the same
83
+ `.rs-*` classes the SSR pages render, so what you see is what
84
+ you'll ship). Save writes `regstack-theme.css`; the designer
85
+ round-trips values back into the form on next launch so iteration
86
+ is non-destructive. Lives in `src/regstack/wizard/theme_designer/`,
87
+ registered via a `_LazyThemeGroup` mirroring the OAuth pattern.
88
+ Same four-layer test stack (validators / writer / routes /
89
+ Playwright e2e). The shared scaffold (FastAPI app, uvicorn launcher,
90
+ pywebview shim, e2e fixture pattern) is duplicated rather than
91
+ abstracted into a base class — the two tools have meaningfully
92
+ different inputs and an early shared base would calcify the wrong
93
+ decisions. Refactor only when a third pywebview tool lands.
80
94
 
81
95
  ## Three kinds of single-use proof
82
96
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: regstack
3
- Version: 0.4.0
3
+ Version: 0.5.0
4
4
  Summary: Embeddable user registration, login, and account management for FastAPI apps. SQLite / Postgres / MongoDB.
5
5
  Project-URL: Homepage, https://github.com/jdrumgoole/regstack
6
6
  Project-URL: Repository, https://github.com/jdrumgoole/regstack
@@ -143,7 +143,7 @@ result everywhere is what regstack is for.
143
143
  ✔ Server-rendered HTML pages, theme with one CSS file
144
144
  ✔ Pluggable email (console / SMTP / Amazon SES) and SMS (Amazon SNS / Twilio)
145
145
  ✔ Argon2 password hashing, CSP-friendly templates
146
- ✔ Setup wizard (`regstack init`) and config validator (`regstack doctor`)
146
+ ✔ Setup wizards (live in their own pywebview windows): `regstack init` (project bootstrap), `regstack oauth setup` (guided Google OAuth client config), `regstack theme design` (live theme designer with preview), and `regstack doctor` (config validator)
147
147
  ✔ Three storage backends: SQLite, PostgreSQL, MongoDB — chosen by URL
148
148
  ```
149
149
 
@@ -186,7 +186,7 @@ register from the command line:
186
186
  ```bash
187
187
  curl -X POST http://localhost:8000/api/auth/register \
188
188
  -H 'content-type: application/json' \
189
- -d '{"email":"alice@example.com","password":"hunter2hunter2","full_name":"Alice"}'
189
+ -d '{"email":"alice@app.example.com","password":"<password>","full_name":"Alice"}'
190
190
  ```
191
191
 
192
192
  The bundled example serves themed SSR pages at `/account/*`, prints
@@ -251,8 +251,8 @@ The same docs are also browsable as Markdown in [`docs/`](https://github.com/jdr
251
251
  Alpha. Single-file SQLite is the default and runs with no infrastructure;
252
252
  PostgreSQL and MongoDB backends pass the same parametrized integration
253
253
  suite. OAuth (Google) shipped in `v0.3.0`; the `regstack oauth setup`
254
- guided wizard shipped in `v0.4.0`. Latest tagged release: `v0.4.0`.
255
- See the
254
+ guided wizard in `v0.4.0`; the live `regstack theme design` tool in
255
+ `v0.5.0`. Latest tagged release: `v0.5.0`. See the
256
256
  [changelog](https://regstack.readthedocs.io/en/latest/changelog.html)
257
257
  for the per-release breakdown.
258
258
 
@@ -72,7 +72,7 @@ result everywhere is what regstack is for.
72
72
  ✔ Server-rendered HTML pages, theme with one CSS file
73
73
  ✔ Pluggable email (console / SMTP / Amazon SES) and SMS (Amazon SNS / Twilio)
74
74
  ✔ Argon2 password hashing, CSP-friendly templates
75
- ✔ Setup wizard (`regstack init`) and config validator (`regstack doctor`)
75
+ ✔ Setup wizards (live in their own pywebview windows): `regstack init` (project bootstrap), `regstack oauth setup` (guided Google OAuth client config), `regstack theme design` (live theme designer with preview), and `regstack doctor` (config validator)
76
76
  ✔ Three storage backends: SQLite, PostgreSQL, MongoDB — chosen by URL
77
77
  ```
78
78
 
@@ -115,7 +115,7 @@ register from the command line:
115
115
  ```bash
116
116
  curl -X POST http://localhost:8000/api/auth/register \
117
117
  -H 'content-type: application/json' \
118
- -d '{"email":"alice@example.com","password":"hunter2hunter2","full_name":"Alice"}'
118
+ -d '{"email":"alice@app.example.com","password":"<password>","full_name":"Alice"}'
119
119
  ```
120
120
 
121
121
  The bundled example serves themed SSR pages at `/account/*`, prints
@@ -180,8 +180,8 @@ The same docs are also browsable as Markdown in [`docs/`](https://github.com/jdr
180
180
  Alpha. Single-file SQLite is the default and runs with no infrastructure;
181
181
  PostgreSQL and MongoDB backends pass the same parametrized integration
182
182
  suite. OAuth (Google) shipped in `v0.3.0`; the `regstack oauth setup`
183
- guided wizard shipped in `v0.4.0`. Latest tagged release: `v0.4.0`.
184
- See the
183
+ guided wizard in `v0.4.0`; the live `regstack theme design` tool in
184
+ `v0.5.0`. Latest tagged release: `v0.5.0`. See the
185
185
  [changelog](https://regstack.readthedocs.io/en/latest/changelog.html)
186
186
  for the per-release breakdown.
187
187
 
@@ -3,6 +3,33 @@
3
3
  All notable changes to this project are documented here. Versions follow
4
4
  [Semantic Versioning](https://semver.org/) once `1.0.0` ships.
5
5
 
6
+ ## 0.5.0 — 2026-05-02
7
+
8
+ ### Added
9
+
10
+ - **Theme designer.** `regstack theme design` opens a native pywebview
11
+ window with controls for every `--rs-*` CSS custom property and a
12
+ real-time preview of the bundled SSR widgets (sign-in form, success
13
+ / error banners, danger-zone button). Saving writes
14
+ `regstack-theme.css`; the designer round-trips values back into the
15
+ form on next launch so iteration is non-destructive. `--print-only`
16
+ mode takes repeatable `--var NAME=VALUE` pairs (with a `dark:`
17
+ prefix for dark-scheme overrides) and writes the file headlessly.
18
+ Lives in `regstack.wizard.theme_designer`; registered as a lazy
19
+ Click subgroup so `regstack init` / `doctor` don't pay the
20
+ pywebview/uvicorn import cost.
21
+ - "Why use regstack" pitch in `docs/index.md` updated to surface the
22
+ two pywebview tools (`oauth setup` + `theme design`) as a
23
+ distinguishing feature vs. fastapi-users / Auth0 / Keycloak.
24
+
25
+ ### Docs
26
+
27
+ - New "About the examples" convention block at the top of
28
+ `docs/index.md`. Every URL, email, smtp host, and admin command
29
+ across the docs now extrapolates from the same fictional app at
30
+ `app.example.com` with `<username>` / `<password>` placeholders —
31
+ no more `user:pw@host/dbname` / `db.internal/myapp` mishmash.
32
+
6
33
  ## 0.4.0 — 2026-05-02
7
34
 
8
35
  ### Added
@@ -0,0 +1,187 @@
1
+ # CLI reference
2
+
3
+ `regstack` is the entry point installed by the package. All sub-commands
4
+ share a config-loading model: programmatic kwargs > env vars >
5
+ `regstack.secrets.env` > `regstack.toml` > defaults. The `--config <path>`
6
+ flag overrides where the TOML file is found.
7
+
8
+ ## `regstack init`
9
+
10
+ Interactive wizard that writes `regstack.toml` and `regstack.secrets.env`
11
+ in the current directory. Asks which backend to use (SQLite default →
12
+ Postgres → MongoDB), generates a 64-byte JWT secret, runs DNS sanity
13
+ checks if asked, never provisions infrastructure.
14
+
15
+ ```bash
16
+ uv run regstack init
17
+ uv run regstack init --target /etc/app --force
18
+ ```
19
+
20
+ Options:
21
+
22
+ - `--target DIR` — directory to write the config files (default cwd).
23
+ - `--force` — overwrite without confirming.
24
+
25
+ Re-running the wizard prompts before overwriting unless `--force` is
26
+ passed; pre-existing answers aren't kept (the wizard is intentionally
27
+ stateless).
28
+
29
+ ## `regstack oauth setup`
30
+
31
+ Opens a guided 12-step wizard in a native webview window that walks you
32
+ through registering a Google OAuth 2.0 client (project selection,
33
+ consent screen, redirect URI, credentials) and merges the result into
34
+ your existing `regstack.toml` and `regstack.secrets.env`. The merge is
35
+ **non-clobbering** — comments, unrelated tables (`[email]`, `[sms]`,
36
+ …), and unrelated top-level keys are preserved. Re-run any time to
37
+ rotate credentials or change the linking policy.
38
+
39
+ ```bash
40
+ uv run regstack oauth setup
41
+ uv run regstack oauth setup --target /etc/app
42
+ ```
43
+
44
+ Options:
45
+
46
+ - `--target DIR` — directory containing (or to receive) `regstack.toml`
47
+ (default cwd).
48
+ - `--api-prefix PREFIX` — router prefix the host mounts regstack under
49
+ (default `/api/auth`). Used to compute the suggested redirect URI.
50
+ - `--port N` — pin the wizard server's TCP port (default: random free
51
+ port on `127.0.0.1`).
52
+ - `--print-only` — skip the GUI; print the TOML + secrets diff that
53
+ *would* be written to stdout, then exit. Useful for headless hosts
54
+ (CI, servers without a webview backend) and dry-run smoke tests.
55
+ Pair with `--client-id`, `--client-secret`, `--base-url`,
56
+ `--auto-link/--no-auto-link`, `--mfa/--no-mfa`.
57
+
58
+ The interactive mode requires a desktop environment with a webview
59
+ backend (WebKit on macOS, GTK / QtWebEngine on Linux, Edge WebView2 on
60
+ Windows). On a headless host it exits with a clear error pointing at
61
+ `--print-only`.
62
+
63
+ The wizard binds to `127.0.0.1` only and authenticates every API call
64
+ with a per-launch random token, so a hostile process on the same host
65
+ can't drive the write endpoint.
66
+
67
+ ## `regstack theme design`
68
+
69
+ Opens a live designer for `regstack-theme.css` in a native pywebview
70
+ window. The left pane has controls for every `--rs-*` CSS custom
71
+ property; the right pane renders the bundled SSR widgets (sign-in
72
+ form, success / error messages, danger-zone button) with your changes
73
+ applied in real time. Click **Save** to write the file; **Reset to
74
+ defaults** to start over; **Copy CSS** to put the generated
75
+ stylesheet on the clipboard without writing.
76
+
77
+ ```bash
78
+ uv run regstack theme design
79
+ uv run regstack theme design --target /var/www/static
80
+ ```
81
+
82
+ Options:
83
+
84
+ - `--target DIR` — directory to write `regstack-theme.css` into
85
+ (default cwd).
86
+ - `--filename NAME` — output filename
87
+ (default `regstack-theme.css`).
88
+ - `--port N` — pin the designer's TCP port (default: random free
89
+ port on `127.0.0.1`).
90
+ - `--print-only` — skip the GUI; write the file from `--var` pairs
91
+ and emit a JSON summary. For headless / CI use.
92
+ - `--var NAME=VALUE` — repeatable. Used with `--print-only`. Prefix
93
+ with `dark:` to set the dark-scheme value, e.g.
94
+ `--var dark:--rs-accent=#2dd4bf`.
95
+
96
+ Re-running the designer reloads the previous values out of the file,
97
+ so iterating on a theme is non-destructive. Only the `:root` and
98
+ `@media (prefers-color-scheme: dark)` blocks are managed by the
99
+ designer — anything else in the file is left alone.
100
+
101
+ Same security shape as `regstack oauth setup`: binds `127.0.0.1`
102
+ only, every API call requires a per-launch random token.
103
+
104
+ ## `regstack create-admin`
105
+
106
+ Create or promote a superuser. Idempotent.
107
+
108
+ ```bash
109
+ uv run regstack create-admin --email admin@app.example.com
110
+ uv run regstack create-admin --email admin@app.example.com --password 'long-strong-password'
111
+ uv run regstack create-admin --email admin@app.example.com --config /etc/app/regstack.toml
112
+ ```
113
+
114
+ Options:
115
+
116
+ - `--email EMAIL` *(required)*.
117
+ - `--password PW` — if omitted, prompts (with confirmation).
118
+ - `--config PATH` — TOML file to load (default: env or cwd).
119
+
120
+ If the user already exists, the command sets `is_superuser=True` and
121
+ keeps the existing password. If they don't exist, it creates the user
122
+ with `is_active=True`, `is_verified=True`, `is_superuser=True` and the
123
+ provided password.
124
+
125
+ ## `regstack migrate`
126
+
127
+ Runs the bundled Alembic migrations against the configured
128
+ `database_url`. Idempotent — re-running on a DB already at the target
129
+ revision is a no-op. Use this on SQL backends (SQLite / PostgreSQL)
130
+ to roll the schema forward to a new regstack release.
131
+
132
+ ```bash
133
+ uv run regstack migrate
134
+ uv run regstack migrate --target head
135
+ uv run regstack migrate --config /etc/app/regstack.toml --target 0001
136
+ ```
137
+
138
+ Options:
139
+
140
+ - `--config PATH` — TOML file to load (default: cwd / `$REGSTACK_CONFIG`).
141
+ - `--target REV` — revision to upgrade to (default `head`). Accepts
142
+ any Alembic revision spec: a revision id (`0001`), a relative step
143
+ (`+1`), or `head`.
144
+
145
+ Mongo backends are silently skipped: TTL indexes are installed by
146
+ `RegStack.install_schema()` on every app start, so there's no separate
147
+ migration story to run. Output prints the before / after revision and
148
+ exits non-zero if Alembic raises.
149
+
150
+ ## `regstack doctor`
151
+
152
+ Runs read-only validation against the loaded config and reports each
153
+ check on a green/red line. Exit code is the number of failed checks —
154
+ suitable for use in container health checks.
155
+
156
+ ```bash
157
+ uv run regstack doctor
158
+ uv run regstack doctor --config /etc/app/regstack.toml
159
+ uv run regstack doctor --check-dns
160
+ uv run regstack doctor --send-test-email alice@app.example.com
161
+ ```
162
+
163
+ Options:
164
+
165
+ - `--config PATH` — TOML file to load.
166
+ - `--check-dns` — run SPF / DMARC / MX `dig`s on the sender domain.
167
+ Internet-dependent; off by default.
168
+ - `--send-test-email TO` — actually send a probe email through the
169
+ configured backend. Costs real money on SES; off by default.
170
+
171
+ Default checks:
172
+
173
+ | Check | Pass criterion |
174
+ |-------|----------------|
175
+ | `jwt secret` | Present, ≥ 32 chars |
176
+ | `backend` | `Backend.ping()` succeeds (works for any backend) |
177
+ | `schema` | Mongo: required indexes present. SQL: `users` table responds to `count(*)` |
178
+ | `email backend` | `build_email_service(config.email)` instantiates |
179
+
180
+ Optional checks:
181
+
182
+ | Check | Pass criterion |
183
+ |-------|----------------|
184
+ | `dns mx` | At least one MX record on the sender domain |
185
+ | `dns spf` | A TXT record containing `v=spf1` |
186
+ | `dns dmarc` | A TXT record at `_dmarc.<domain>` containing `v=DMARC1` |
187
+ | `email send` | The configured backend's `send()` returned without raising |
@@ -85,15 +85,16 @@ regstack picks a backend at construction time from the URL scheme of
85
85
  - Notes
86
86
 
87
87
  * - SQLite
88
- - `sqlite+aiosqlite:///./path.db`
88
+ - `sqlite+aiosqlite:///./dbname.db`
89
89
  - Default. Bundled in the base install — no extras needed.
90
90
  `:memory:` works too (per-test).
91
91
  * - Postgres
92
- - `postgresql+asyncpg://user:pw@host/dbname`
92
+ - `postgresql+asyncpg://<username>:<password>@dbhost.example.com:5432/dbname`
93
93
  - Requires the `postgres` extra (pulls in `asyncpg`). The driver is
94
94
  pinned to `+asyncpg` — sync drivers won't work.
95
95
  * - MongoDB
96
- - `mongodb://host:port/dbname` (or `mongodb+srv://`)
96
+ - `mongodb://<username>:<password>@dbhost.example.com:27017/dbname`
97
+ (or `mongodb+srv://<username>:<password>@app.abc123.mongodb.net/dbname`)
97
98
  - Requires the `mongo` extra (pulls in `pymongo`). Database is taken
98
99
  from the URL path; falls back to ``mongodb_database`` if absent.
99
100
  ```
@@ -234,14 +235,14 @@ The active backend exposes the same five repository protocols on
234
235
  ```toml
235
236
  [email]
236
237
  backend = "console" # console | smtp | ses
237
- from_address = "noreply@…"
238
- from_name = "MyApp"
238
+ from_address = "noreply@app.example.com"
239
+ from_name = "Example App"
239
240
 
240
241
  # smtp
241
- smtp_host = "smtp.example.com"
242
+ smtp_host = "smtp.app.example.com"
242
243
  smtp_port = 587
243
244
  smtp_starttls = true
244
- smtp_username = "myapp"
245
+ smtp_username = "<username>"
245
246
  # smtp_password is a SecretStr — set via REGSTACK_EMAIL__SMTP_PASSWORD
246
247
 
247
248
  # ses
@@ -16,13 +16,13 @@ a working backend with no infrastructure. To switch:
16
16
  # regstack.toml
17
17
 
18
18
  # SQLite (default — file lives wherever the path points)
19
- database_url = "sqlite+aiosqlite:///./regstack.db"
19
+ database_url = "sqlite+aiosqlite:///./dbname.db"
20
20
 
21
21
  # Postgres (needs the `postgres` extra → asyncpg)
22
- database_url = "postgresql+asyncpg://user:pw@db.internal/myapp"
22
+ database_url = "postgresql+asyncpg://<username>:<password>@dbhost.example.com:5432/dbname"
23
23
 
24
24
  # MongoDB (needs the `mongo` extra → pymongo)
25
- database_url = "mongodb://db.internal:27017/myapp"
25
+ database_url = "mongodb://<username>:<password>@dbhost.example.com:27017/dbname"
26
26
  ```
27
27
 
28
28
  Hosts that already manage their own connection pool — for example, an
@@ -210,7 +210,7 @@ one does not validate against the other. The
210
210
  ## Bootstrapping the first admin
211
211
 
212
212
  ```bash
213
- uv run regstack create-admin --email admin@example.com
213
+ uv run regstack create-admin --email admin@app.example.com
214
214
  ```
215
215
 
216
216
  The CLI prompts for a password (with confirmation). Re-running with
@@ -220,7 +220,7 @@ their password.
220
220
  In code:
221
221
 
222
222
  ```python
223
- await regstack.bootstrap_admin("admin@example.com", "long-strong-password")
223
+ await regstack.bootstrap_admin("admin@app.example.com", "long-strong-password")
224
224
  ```
225
225
 
226
226
  This is idempotent — promotes an existing user, creates one if not
@@ -63,6 +63,52 @@ and keep the user table in your own database** — not "stand up a
63
63
  separate auth product" and not "write the boring 80% from scratch
64
64
  each time".
65
65
 
66
+ ### What's distinctive about regstack
67
+
68
+ - **Native pywebview setup tools, not just Click prompts.**
69
+ - `regstack oauth setup` opens a real desktop window that walks an
70
+ operator through registering a Google OAuth 2.0 client and
71
+ merging the credentials into `regstack.toml` non-clobberingly —
72
+ no copy-pasting the redirect URI four times.
73
+ - `regstack theme design` opens a live theme designer with a
74
+ real-time preview of the SSR auth pages: tweak a colour, see
75
+ the Sign-in card update on the right; click Save to write
76
+ `regstack-theme.css`. CSS-only re-skin in minutes, no template
77
+ editing required.
78
+ Both tools are local-only (`127.0.0.1` + per-launch token), have
79
+ a `--print-only` mode for headless CI, and ship under the same
80
+ test-from-the-outside Playwright suite the SSR pages do.
81
+ - **Zero vendor lock-in.** Your user table is in your database, not
82
+ someone else's. Switch storage backends by changing one URL.
83
+ - **Opt-in everything.** No SMS extra → no `twilio` / `aioboto3`
84
+ install. No SES → no AWS SDK. No SSR → no Jinja templates loaded.
85
+ The base install is small.
86
+
87
+ ## About the examples
88
+
89
+ To keep URLs and config values consistent across the docs, every
90
+ example pretends to embed regstack into a fictional app hosted at
91
+ `app.example.com`. Throughout the docs:
92
+
93
+ | What | Value |
94
+ |---|---|
95
+ | Public host | `app.example.com` |
96
+ | `base_url` | `https://app.example.com` |
97
+ | Database host (prod) | `dbhost.example.com` |
98
+ | Database user | `<username>` |
99
+ | Database password | `<password>` |
100
+ | Database name | `dbname` |
101
+ | Email sender | `noreply@app.example.com` |
102
+ | Local dev port | `localhost:8000` |
103
+
104
+ So a Postgres URL looks like
105
+ `postgresql+asyncpg://<username>:<password>@dbhost.example.com:5432/dbname`,
106
+ a MongoDB URL like
107
+ `mongodb://<username>:<password>@dbhost.example.com:27017/dbname`,
108
+ and the local SQLite path `sqlite+aiosqlite:///./dbname.db`. Substitute
109
+ your own values when copying — the shape is the only thing that
110
+ matters.
111
+
66
112
  ## What's in the box
67
113
 
68
114
  - **Three storage backends, one API.** SQLite (the default — single
@@ -76,7 +122,12 @@ each time".
76
122
  forgot, reset, MFA confirm, account dashboard. Themed via CSS
77
123
  custom properties — no template editing required for a re-skin.
78
124
  Full template overrides are still possible per host.
79
- - **CLIs.** `regstack init` (interactive setup wizard),
125
+ - **CLIs + native pywebview tools.**
126
+ `regstack init` (interactive project bootstrap),
127
+ `regstack oauth setup` (guided Google OAuth client configuration in
128
+ a native webview — see [OAuth guide](oauth.md)),
129
+ `regstack theme design` (live theme designer with a real-time preview
130
+ of the SSR widgets — see [theming guide](theming.md)),
80
131
  `regstack create-admin`, `regstack doctor`.
81
132
  - **OAuth — Sign in with Google** (opt-in, since 0.3.0). Authorization
82
133
  Code with PKCE, ID-token verification, identity-linking with a
@@ -139,7 +190,7 @@ changelog
139
190
 
140
191
  ## Project status
141
192
 
142
- Alpha. Latest tagged release: `v0.4.0`. SQLite is the default and
193
+ Alpha. Latest tagged release: `v0.5.0`. SQLite is the default and
143
194
  runs with no infrastructure; PostgreSQL and MongoDB pass the same
144
195
  parametrized integration suite. The full backend matrix runs in
145
196
  parallel against every test, so a green CI on `main` is a strong
@@ -144,7 +144,7 @@ uv run uvicorn examples.sqlite.main:app --reload
144
144
  # In another terminal
145
145
  curl -X POST http://localhost:8000/api/auth/register \
146
146
  -H 'content-type: application/json' \
147
- -d '{"email":"a@example.com","password":"hunter2hunter2","full_name":"A"}'
147
+ -d '{"email":"alice@app.example.com","password":"<password>","full_name":"Alice"}'
148
148
  ```
149
149
 
150
150
  The SQLite demo enables verification by default. Look at the demo's
@@ -4,6 +4,30 @@ The bundled UI ships a structural stylesheet (`core.css`) and a default
4
4
  theme stylesheet (`theme.css`). Hosts pick one of three integration
5
5
  levels depending on how much they want to override.
6
6
 
7
+ ## Quickest path: the live designer
8
+
9
+ ```bash
10
+ uv run regstack theme design
11
+ ```
12
+
13
+ Opens a native pywebview window with controls for every `--rs-*`
14
+ variable on the left and a real-time preview of the bundled SSR
15
+ widgets on the right. Tweak a colour, watch the Sign-in card update;
16
+ click Save to write `regstack-theme.css`. There's a `--print-only`
17
+ mode for headless / CI use:
18
+
19
+ ```bash
20
+ uv run regstack theme design --print-only \
21
+ --var --rs-accent=#0d9488 \
22
+ --var --rs-radius=10 \
23
+ --var dark:--rs-accent=#2dd4bf
24
+ ```
25
+
26
+ Re-running the designer reloads your previous values back into the
27
+ form, so you can iterate without losing state. The rest of this guide
28
+ explains the underlying override mechanisms — useful when you want
29
+ to go beyond what the designer exposes.
30
+
7
31
  ## Level 1 — swap one stylesheet
8
32
 
9
33
  Set `config.theme_css_url` to a URL where your host serves a custom
@@ -34,9 +58,30 @@ Your `static/my-theme.css`:
34
58
  ```
35
59
 
36
60
  That's it — every regstack page picks up the new palette. See
37
- `examples/minimal/branding/theme.css` for a working wine-themed
61
+ `examples/mongo/branding/theme.css` for a working wine-themed
38
62
  example.
39
63
 
64
+ To support both light and dark mode, add a `prefers-color-scheme`
65
+ block re-declaring whichever variables need to differ:
66
+
67
+ ```css
68
+ :root {
69
+ --rs-accent: #0d9488;
70
+ --rs-accent-bg: rgba(13, 148, 136, 0.08);
71
+ }
72
+
73
+ @media (prefers-color-scheme: dark) {
74
+ :root {
75
+ --rs-accent: #2dd4bf;
76
+ --rs-accent-bg: rgba(45, 212, 191, 0.12);
77
+ }
78
+ }
79
+ ```
80
+
81
+ The bundled `theme.css` already supplies dark-mode defaults for every
82
+ variable, so you only need to override the ones whose dark variant
83
+ differs from the auto-derived contrast.
84
+
40
85
  ## Variables
41
86
 
42
87
  ```{list-table}
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "regstack"
3
- version = "0.4.0"
3
+ version = "0.5.0"
4
4
  description = "Embeddable user registration, login, and account management for FastAPI apps. SQLite / Postgres / MongoDB."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
@@ -28,6 +28,20 @@ class _LazyOauthGroup(click.Group):
28
28
  return setup_cmd
29
29
 
30
30
 
31
+ class _LazyThemeGroup(click.Group):
32
+ """Same lazy-import pattern for the theme designer subtree."""
33
+
34
+ def list_commands(self, ctx: click.Context) -> list[str]:
35
+ return ["design"]
36
+
37
+ def get_command(self, ctx: click.Context, name: str) -> click.Command | None:
38
+ if name != "design":
39
+ return None
40
+ from regstack.wizard.theme_designer.cli import design as design_cmd
41
+
42
+ return design_cmd
43
+
44
+
31
45
  @click.group(help="regstack — embeddable account registration for FastAPI apps.")
32
46
  @click.version_option(__version__, prog_name="regstack")
33
47
  def cli() -> None:
@@ -39,6 +53,7 @@ cli.add_command(create_admin_cmd)
39
53
  cli.add_command(doctor_cmd)
40
54
  cli.add_command(migrate_cmd)
41
55
  cli.add_command(_LazyOauthGroup(name="oauth", help="OAuth provider setup wizards."))
56
+ cli.add_command(_LazyThemeGroup(name="theme", help="Theme designer for the SSR pages."))
42
57
 
43
58
 
44
59
  def main() -> None:
@@ -0,0 +1 @@
1
+ __version__ = "0.5.0"
@@ -0,0 +1,8 @@
1
+ """Interactive theme designer for regstack's SSR pages.
2
+
3
+ ``regstack theme design`` opens a native pywebview window with a live
4
+ preview of the bundled SSR widgets and a panel of controls for the
5
+ ``--rs-*`` CSS custom properties. Saving writes a single
6
+ ``regstack-theme.css`` the host can serve and reference via
7
+ ``config.theme_css_url``.
8
+ """