django-admin-react 1.4.11__tar.gz → 1.4.13__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 (31) hide show
  1. {django_admin_react-1.4.11 → django_admin_react-1.4.13}/PKG-INFO +122 -3
  2. {django_admin_react-1.4.11 → django_admin_react-1.4.13}/README.md +118 -1
  3. {django_admin_react-1.4.11 → django_admin_react-1.4.13}/django_admin_react/conf.py +17 -4
  4. {django_admin_react-1.4.11 → django_admin_react-1.4.13}/django_admin_react/static/admin_react/.vite/manifest.json +4 -4
  5. django_admin_react-1.4.11/django_admin_react/static/admin_react/assets/ColumnLayoutModal-Bo2HCky2.js → django_admin_react-1.4.13/django_admin_react/static/admin_react/assets/ColumnLayoutModal-Bqf6vN1j.js +1 -1
  6. django_admin_react-1.4.11/django_admin_react/static/admin_react/assets/JsonViewer-CH3aXJXg.js → django_admin_react-1.4.13/django_admin_react/static/admin_react/assets/JsonViewer-DkeJkG12.js +1 -1
  7. django_admin_react-1.4.13/django_admin_react/static/admin_react/assets/index-CGh8t5dj.js +8 -0
  8. django_admin_react-1.4.13/django_admin_react/static/admin_react/assets/index-CpxUkcdm.css +1 -0
  9. {django_admin_react-1.4.11 → django_admin_react-1.4.13}/django_admin_react/static/admin_react/index.html +2 -2
  10. {django_admin_react-1.4.11 → django_admin_react-1.4.13}/django_admin_react/views.py +21 -6
  11. {django_admin_react-1.4.11 → django_admin_react-1.4.13}/pyproject.toml +1 -1
  12. django_admin_react-1.4.11/django_admin_react/static/admin_react/.gitkeep +0 -0
  13. django_admin_react-1.4.11/django_admin_react/static/admin_react/README.md +0 -15
  14. django_admin_react-1.4.11/django_admin_react/static/admin_react/assets/index-Bw0i616L.js +0 -8
  15. django_admin_react-1.4.11/django_admin_react/static/admin_react/assets/index-DRQ2gAuA.css +0 -1
  16. {django_admin_react-1.4.11 → django_admin_react-1.4.13}/LICENSE +0 -0
  17. {django_admin_react-1.4.11 → django_admin_react-1.4.13}/django_admin_react/README.md +0 -0
  18. {django_admin_react-1.4.11 → django_admin_react-1.4.13}/django_admin_react/__init__.py +0 -0
  19. {django_admin_react-1.4.11 → django_admin_react-1.4.13}/django_admin_react/apps.py +0 -0
  20. {django_admin_react-1.4.11 → django_admin_react-1.4.13}/django_admin_react/audit.py +0 -0
  21. {django_admin_react-1.4.11 → django_admin_react-1.4.13}/django_admin_react/pwa.py +0 -0
  22. {django_admin_react-1.4.11 → django_admin_react-1.4.13}/django_admin_react/templates/README.md +0 -0
  23. {django_admin_react-1.4.11 → django_admin_react-1.4.13}/django_admin_react/templates/admin/base_site.html +0 -0
  24. {django_admin_react-1.4.11 → django_admin_react-1.4.13}/django_admin_react/templates/admin_react/README.md +0 -0
  25. {django_admin_react-1.4.11 → django_admin_react-1.4.13}/django_admin_react/templates/admin_react/index.html +0 -0
  26. {django_admin_react-1.4.11 → django_admin_react-1.4.13}/django_admin_react/templates/admin_react/login.html +0 -0
  27. {django_admin_react-1.4.11 → django_admin_react-1.4.13}/django_admin_react/templates/admin_react/sw.js +0 -0
  28. {django_admin_react-1.4.11 → django_admin_react-1.4.13}/django_admin_react/templates/django_admin_react/_experience_toggle_strip.html +0 -0
  29. {django_admin_react-1.4.11 → django_admin_react-1.4.13}/django_admin_react/templatetags/__init__.py +0 -0
  30. {django_admin_react-1.4.11 → django_admin_react-1.4.13}/django_admin_react/templatetags/experience_toggle.py +0 -0
  31. {django_admin_react-1.4.11 → django_admin_react-1.4.13}/django_admin_react/urls.py +0 -0
@@ -1,8 +1,9 @@
1
- Metadata-Version: 2.3
1
+ Metadata-Version: 2.4
2
2
  Name: django-admin-react
3
- Version: 1.4.11
3
+ Version: 1.4.13
4
4
  Summary: A drop-in React single-page admin for Django, driven entirely by ModelAdmin.
5
5
  License: MIT
6
+ License-File: LICENSE
6
7
  Keywords: django,admin,react,spa,tailwind
7
8
  Author: django-admin-react contributors
8
9
  Requires-Python: >=3.10,<4.0
@@ -21,6 +22,7 @@ Classifier: Programming Language :: Python :: 3.10
21
22
  Classifier: Programming Language :: Python :: 3.11
22
23
  Classifier: Programming Language :: Python :: 3.12
23
24
  Classifier: Programming Language :: Python :: 3.13
25
+ Classifier: Programming Language :: Python :: 3.14
24
26
  Classifier: Topic :: Internet :: WWW/HTTP :: Site Management
25
27
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
26
28
  Requires-Dist: django (>=5.0,<7.0)
@@ -618,7 +620,9 @@ all share the v1 wire contract. Per-feature live status below.
618
620
  | `date_hierarchy` | ✅ |
619
621
  | `list_editable` + bulk PATCH | ✅ |
620
622
  | `actions` — batch + detail (signature-classified) | ✅ |
621
- | `autocomplete_fields` / `raw_id_fields` | ✅ |
623
+ | `autocomplete_fields` | ✅ |
624
+ | `raw_id_fields` (pk text input + lookup popup) | 🟡 [#626](https://github.com/MartinCastroAlvarez/django-admin-react/issues/626) (API emits the hint; SPA still renders autocomplete) |
625
+ | `radio_fields` (inline radio buttons vs `<select>`) | 🟡 [#626](https://github.com/MartinCastroAlvarez/django-admin-react/issues/626) (API emits the hint; SPA still renders dropdown) |
622
626
  | `ManyToManyField` read + write | ✅ |
623
627
  | `inlines` (TabularInline / StackedInline) — read + write | ✅ |
624
628
  | `FileField` / `ImageField` — read | ✅ |
@@ -634,6 +638,121 @@ all share the v1 wire contract. Per-feature live status below.
634
638
 
635
639
  ✅ = shipped. 🟡 = not yet built (tracked).
636
640
 
641
+ ### Stock-Django `ModelAdmin` hooks that do NOT carry through to the SPA
642
+
643
+ The SPA renders from the JSON wire — it never sees the consumer's
644
+ Django HTML templates, custom widgets, or `get_urls()` views. The
645
+ hooks below are stock-Django extension points the SPA cannot honour
646
+ today; if your admin uses any of them, the surface behaves
647
+ differently on the SPA than on the legacy `/admin/`. Tracking
648
+ issues link the work to close each gap.
649
+
650
+ | Stock-Django hook | SPA behaviour | Tracked |
651
+ |---|---|---|
652
+ | `change_form_template` / `change_list_template` / `add_form_template` / `change_password_template` / `object_history_template` overrides | Silently ignored — the SPA renders entirely from the JSON wire. | [#624](https://github.com/MartinCastroAlvarez/django-admin-react/issues/624) |
653
+ | `formfield_overrides = {Field: {"widget": CustomWidget}}` | Custom widget invisible — the SPA picks its own control from the field's `type`. No React-side widget-registration API yet. | [#625](https://github.com/MartinCastroAlvarez/django-admin-react/issues/625) |
654
+ | `raw_id_fields` | Falls back to the autocomplete picker (same as `autocomplete_fields`). Defeats the purpose for FKs with 10M+ rows where autocomplete `get_search_results` is too expensive. | [#626](https://github.com/MartinCastroAlvarez/django-admin-react/issues/626) |
655
+ | `radio_fields = {"status": admin.HORIZONTAL}` | Renders a `<select>` (default choice control) instead of inline radio buttons. | [#626](https://github.com/MartinCastroAlvarez/django-admin-react/issues/626) |
656
+ | `filter_horizontal` / `filter_vertical` (M2M shuttle widget) | Renders the generic multi-select checkbox list, not Django's two-pane shuttle. Switch the field to `autocomplete_fields` for a workable SPA UX. | [#627](https://github.com/MartinCastroAlvarez/django-admin-react/issues/627) |
657
+ | `GenericForeignKey` / `GenericInlineModelAdmin` | Support gap — verify per-model before relying on the SPA. | [#628](https://github.com/MartinCastroAlvarez/django-admin-react/issues/628) |
658
+ | `LANGUAGE_CODE` / `gettext` / `Accept-Language` | The SPA chrome stays English; translated `verbose_name` / `help_text` / `@admin.action(description=_("..."))` are not surfaced per-request. | [#630](https://github.com/MartinCastroAlvarez/django-admin-react/issues/630) |
659
+ | `ModelAdmin.get_urls()` custom views | Opens as a popout (`<a target="_blank">`) into the Django-rendered HTML page — no SPA chrome, no breadcrumb. The link IS surfaced; the UX is just outside the SPA. | [#623](https://github.com/MartinCastroAlvarez/django-admin-react/issues/623) |
660
+ | Django 4.2 LTS support | Not yet — the package pins `django >= 5.0,<7.0`. | [#622](https://github.com/MartinCastroAlvarez/django-admin-react/issues/622) |
661
+
662
+ If your admin relies on any "silently ignored" hook above, the
663
+ typical workaround is to keep that model on the legacy
664
+ `/admin/` surface via the
665
+ [experience-toggle strip](#experience-toggle-strip-optional) — the
666
+ SPA + legacy admin happily coexist.
667
+
668
+ ---
669
+
670
+ ## Writing safe `list_display` callables
671
+
672
+ This applies on **both** the legacy `/admin/` and the SPA — but the
673
+ SPA renders any `format_html` / `mark_safe` value via React's
674
+ `dangerouslySetInnerHTML`, so misuse is reflected XSS the same way
675
+ the legacy admin would be.
676
+
677
+ **Do not** interpolate user-controlled data into a `mark_safe(...)`
678
+ string. The whole point of `mark_safe` is "I have already escaped
679
+ this," and `f"<span>{obj.user_input}</span>"` has not — so a
680
+ `user_input` of `<script>alert(1)</script>` runs.
681
+
682
+ ```python
683
+ # WRONG — copy-paste-from-StackOverflow XSS hazard.
684
+ @admin.display(description="Status")
685
+ def status_badge(self, obj):
686
+ return mark_safe(f'<span class="badge">{obj.user_input}</span>')
687
+
688
+ # RIGHT — format_html auto-escapes every interpolated arg.
689
+ @admin.display(description="Status")
690
+ def status_badge(self, obj):
691
+ return format_html('<span class="badge">{}</span>', obj.user_input)
692
+ ```
693
+
694
+ Same rule for `readonly_fields` callables. See
695
+ [#633](https://github.com/MartinCastroAlvarez/django-admin-react/issues/633)
696
+ for the optional defense-in-depth `STRICT_HTML` setting tracking
697
+ issue (bleach-clean every rendered HTML value with a tight allow-list).
698
+
699
+ ---
700
+
701
+ ## Hardening
702
+
703
+ ### Brute-force defense on `/api/v1/login/`
704
+
705
+ The package's React login endpoint (`<mount>/api/v1/login/`) reuses
706
+ Django's session auth, so the canonical brute-force defenses work
707
+ unchanged. The recommended layer is
708
+ [`django-axes`](https://pypi.org/project/django-axes/):
709
+
710
+ ```python
711
+ # settings.py
712
+ INSTALLED_APPS = [..., "axes", "django_admin_react", "django_admin_rest_api"]
713
+
714
+ AUTHENTICATION_BACKENDS = [
715
+ "axes.backends.AxesStandaloneBackend",
716
+ "django.contrib.auth.backends.ModelBackend",
717
+ ]
718
+ MIDDLEWARE = [..., "axes.middleware.AxesMiddleware"]
719
+
720
+ AXES_FAILURE_LIMIT = 5
721
+ AXES_COOLOFF_TIME = 1 # hour
722
+ ```
723
+
724
+ Axes intercepts via `AUTHENTICATION_BACKENDS`, not URL middleware, so
725
+ lockouts apply to both the legacy admin login and the SPA's JSON
726
+ login automatically. Tracked: [#634](https://github.com/MartinCastroAlvarez/django-admin-react/issues/634).
727
+
728
+ ### Mounting the API on a different origin (CORS + cookies)
729
+
730
+ `DJANGO_ADMIN_REACT["API_URL_PREFIX"]` lets the SPA point at a
731
+ separately-mounted REST API — e.g. SPA at `admin.example.com`
732
+ talking to an API at `api.example.com`. The session-cookie auth
733
+ across origins needs three settings configured together; if any
734
+ one is missing, every API call silently 401s after login.
735
+
736
+ ```python
737
+ # settings.py — required when SPA and API are on different origins.
738
+ SESSION_COOKIE_SAMESITE = "None" # default "Lax" drops cookies cross-origin
739
+ SESSION_COOKIE_SECURE = True # required by browsers when SameSite=None
740
+ CSRF_COOKIE_SAMESITE = "None"
741
+ CSRF_COOKIE_SECURE = True
742
+
743
+ # pip install django-cors-headers
744
+ INSTALLED_APPS = [..., "corsheaders", ...]
745
+ MIDDLEWARE = ["corsheaders.middleware.CorsMiddleware", ...]
746
+
747
+ CORS_ALLOW_CREDENTIALS = True
748
+ CORS_ALLOWED_ORIGINS = ["https://admin.example.com"] # NEVER "*" with credentials
749
+ CSRF_TRUSTED_ORIGINS = ["https://admin.example.com"]
750
+ ```
751
+
752
+ The SPA's HTTP client already sends `credentials: "include"`, so no
753
+ frontend change is needed — only the Django-side cookie + CORS
754
+ config above. Tracked: [#635](https://github.com/MartinCastroAlvarez/django-admin-react/issues/635).
755
+
637
756
  ---
638
757
 
639
758
  ## The API surface
@@ -585,7 +585,9 @@ all share the v1 wire contract. Per-feature live status below.
585
585
  | `date_hierarchy` | ✅ |
586
586
  | `list_editable` + bulk PATCH | ✅ |
587
587
  | `actions` — batch + detail (signature-classified) | ✅ |
588
- | `autocomplete_fields` / `raw_id_fields` | ✅ |
588
+ | `autocomplete_fields` | ✅ |
589
+ | `raw_id_fields` (pk text input + lookup popup) | 🟡 [#626](https://github.com/MartinCastroAlvarez/django-admin-react/issues/626) (API emits the hint; SPA still renders autocomplete) |
590
+ | `radio_fields` (inline radio buttons vs `<select>`) | 🟡 [#626](https://github.com/MartinCastroAlvarez/django-admin-react/issues/626) (API emits the hint; SPA still renders dropdown) |
589
591
  | `ManyToManyField` read + write | ✅ |
590
592
  | `inlines` (TabularInline / StackedInline) — read + write | ✅ |
591
593
  | `FileField` / `ImageField` — read | ✅ |
@@ -601,6 +603,121 @@ all share the v1 wire contract. Per-feature live status below.
601
603
 
602
604
  ✅ = shipped. 🟡 = not yet built (tracked).
603
605
 
606
+ ### Stock-Django `ModelAdmin` hooks that do NOT carry through to the SPA
607
+
608
+ The SPA renders from the JSON wire — it never sees the consumer's
609
+ Django HTML templates, custom widgets, or `get_urls()` views. The
610
+ hooks below are stock-Django extension points the SPA cannot honour
611
+ today; if your admin uses any of them, the surface behaves
612
+ differently on the SPA than on the legacy `/admin/`. Tracking
613
+ issues link the work to close each gap.
614
+
615
+ | Stock-Django hook | SPA behaviour | Tracked |
616
+ |---|---|---|
617
+ | `change_form_template` / `change_list_template` / `add_form_template` / `change_password_template` / `object_history_template` overrides | Silently ignored — the SPA renders entirely from the JSON wire. | [#624](https://github.com/MartinCastroAlvarez/django-admin-react/issues/624) |
618
+ | `formfield_overrides = {Field: {"widget": CustomWidget}}` | Custom widget invisible — the SPA picks its own control from the field's `type`. No React-side widget-registration API yet. | [#625](https://github.com/MartinCastroAlvarez/django-admin-react/issues/625) |
619
+ | `raw_id_fields` | Falls back to the autocomplete picker (same as `autocomplete_fields`). Defeats the purpose for FKs with 10M+ rows where autocomplete `get_search_results` is too expensive. | [#626](https://github.com/MartinCastroAlvarez/django-admin-react/issues/626) |
620
+ | `radio_fields = {"status": admin.HORIZONTAL}` | Renders a `<select>` (default choice control) instead of inline radio buttons. | [#626](https://github.com/MartinCastroAlvarez/django-admin-react/issues/626) |
621
+ | `filter_horizontal` / `filter_vertical` (M2M shuttle widget) | Renders the generic multi-select checkbox list, not Django's two-pane shuttle. Switch the field to `autocomplete_fields` for a workable SPA UX. | [#627](https://github.com/MartinCastroAlvarez/django-admin-react/issues/627) |
622
+ | `GenericForeignKey` / `GenericInlineModelAdmin` | Support gap — verify per-model before relying on the SPA. | [#628](https://github.com/MartinCastroAlvarez/django-admin-react/issues/628) |
623
+ | `LANGUAGE_CODE` / `gettext` / `Accept-Language` | The SPA chrome stays English; translated `verbose_name` / `help_text` / `@admin.action(description=_("..."))` are not surfaced per-request. | [#630](https://github.com/MartinCastroAlvarez/django-admin-react/issues/630) |
624
+ | `ModelAdmin.get_urls()` custom views | Opens as a popout (`<a target="_blank">`) into the Django-rendered HTML page — no SPA chrome, no breadcrumb. The link IS surfaced; the UX is just outside the SPA. | [#623](https://github.com/MartinCastroAlvarez/django-admin-react/issues/623) |
625
+ | Django 4.2 LTS support | Not yet — the package pins `django >= 5.0,<7.0`. | [#622](https://github.com/MartinCastroAlvarez/django-admin-react/issues/622) |
626
+
627
+ If your admin relies on any "silently ignored" hook above, the
628
+ typical workaround is to keep that model on the legacy
629
+ `/admin/` surface via the
630
+ [experience-toggle strip](#experience-toggle-strip-optional) — the
631
+ SPA + legacy admin happily coexist.
632
+
633
+ ---
634
+
635
+ ## Writing safe `list_display` callables
636
+
637
+ This applies on **both** the legacy `/admin/` and the SPA — but the
638
+ SPA renders any `format_html` / `mark_safe` value via React's
639
+ `dangerouslySetInnerHTML`, so misuse is reflected XSS the same way
640
+ the legacy admin would be.
641
+
642
+ **Do not** interpolate user-controlled data into a `mark_safe(...)`
643
+ string. The whole point of `mark_safe` is "I have already escaped
644
+ this," and `f"<span>{obj.user_input}</span>"` has not — so a
645
+ `user_input` of `<script>alert(1)</script>` runs.
646
+
647
+ ```python
648
+ # WRONG — copy-paste-from-StackOverflow XSS hazard.
649
+ @admin.display(description="Status")
650
+ def status_badge(self, obj):
651
+ return mark_safe(f'<span class="badge">{obj.user_input}</span>')
652
+
653
+ # RIGHT — format_html auto-escapes every interpolated arg.
654
+ @admin.display(description="Status")
655
+ def status_badge(self, obj):
656
+ return format_html('<span class="badge">{}</span>', obj.user_input)
657
+ ```
658
+
659
+ Same rule for `readonly_fields` callables. See
660
+ [#633](https://github.com/MartinCastroAlvarez/django-admin-react/issues/633)
661
+ for the optional defense-in-depth `STRICT_HTML` setting tracking
662
+ issue (bleach-clean every rendered HTML value with a tight allow-list).
663
+
664
+ ---
665
+
666
+ ## Hardening
667
+
668
+ ### Brute-force defense on `/api/v1/login/`
669
+
670
+ The package's React login endpoint (`<mount>/api/v1/login/`) reuses
671
+ Django's session auth, so the canonical brute-force defenses work
672
+ unchanged. The recommended layer is
673
+ [`django-axes`](https://pypi.org/project/django-axes/):
674
+
675
+ ```python
676
+ # settings.py
677
+ INSTALLED_APPS = [..., "axes", "django_admin_react", "django_admin_rest_api"]
678
+
679
+ AUTHENTICATION_BACKENDS = [
680
+ "axes.backends.AxesStandaloneBackend",
681
+ "django.contrib.auth.backends.ModelBackend",
682
+ ]
683
+ MIDDLEWARE = [..., "axes.middleware.AxesMiddleware"]
684
+
685
+ AXES_FAILURE_LIMIT = 5
686
+ AXES_COOLOFF_TIME = 1 # hour
687
+ ```
688
+
689
+ Axes intercepts via `AUTHENTICATION_BACKENDS`, not URL middleware, so
690
+ lockouts apply to both the legacy admin login and the SPA's JSON
691
+ login automatically. Tracked: [#634](https://github.com/MartinCastroAlvarez/django-admin-react/issues/634).
692
+
693
+ ### Mounting the API on a different origin (CORS + cookies)
694
+
695
+ `DJANGO_ADMIN_REACT["API_URL_PREFIX"]` lets the SPA point at a
696
+ separately-mounted REST API — e.g. SPA at `admin.example.com`
697
+ talking to an API at `api.example.com`. The session-cookie auth
698
+ across origins needs three settings configured together; if any
699
+ one is missing, every API call silently 401s after login.
700
+
701
+ ```python
702
+ # settings.py — required when SPA and API are on different origins.
703
+ SESSION_COOKIE_SAMESITE = "None" # default "Lax" drops cookies cross-origin
704
+ SESSION_COOKIE_SECURE = True # required by browsers when SameSite=None
705
+ CSRF_COOKIE_SAMESITE = "None"
706
+ CSRF_COOKIE_SECURE = True
707
+
708
+ # pip install django-cors-headers
709
+ INSTALLED_APPS = [..., "corsheaders", ...]
710
+ MIDDLEWARE = ["corsheaders.middleware.CorsMiddleware", ...]
711
+
712
+ CORS_ALLOW_CREDENTIALS = True
713
+ CORS_ALLOWED_ORIGINS = ["https://admin.example.com"] # NEVER "*" with credentials
714
+ CSRF_TRUSTED_ORIGINS = ["https://admin.example.com"]
715
+ ```
716
+
717
+ The SPA's HTTP client already sends `credentials: "include"`, so no
718
+ frontend change is needed — only the Django-side cookie + CORS
719
+ config above. Tracked: [#635](https://github.com/MartinCastroAlvarez/django-admin-react/issues/635).
720
+
604
721
  ---
605
722
 
606
723
  ## The API surface
@@ -20,6 +20,12 @@ from typing import Any
20
20
 
21
21
  from django.conf import settings as django_settings
22
22
 
23
+ # Built-in fallback for the ``--dar-primary`` accent color when the
24
+ # consumer hasn't set ``PRIMARY_COLOR`` AND their ``AdminSite`` has no
25
+ # ``site_primary_color`` attribute. Re-exported so ``views.py`` can
26
+ # pick up the same constant instead of stringifying its own.
27
+ DEFAULT_PRIMARY_COLOR = "#2563eb"
28
+
23
29
  DEFAULTS: dict[str, Any] = {
24
30
  "ADMIN_SITE": "django.contrib.admin.site",
25
31
  # The list page size derives from the model's
@@ -59,9 +65,16 @@ DEFAULTS: dict[str, Any] = {
59
65
  # ``--dar-primary`` CSS variable so a consumer can brand the admin with
60
66
  # no React rebuild. Must be a hex color (``#rgb`` / ``#rgba`` /
61
67
  # ``#rrggbb`` / ``#rrggbbaa``); anything else is rejected at render and
62
- # falls back to this default, since the value is written into a
63
- # ``<style>`` block and must not be able to inject CSS.
64
- "PRIMARY_COLOR": "#2563eb",
68
+ # falls back to ``DEFAULT_PRIMARY_COLOR`` below, since the value is
69
+ # written into a ``<style>`` block and must not be able to inject CSS.
70
+ #
71
+ # ``None`` (default) means "consumer didn't explicitly set this" — the
72
+ # SPA reads ``site_primary_color`` off the configured ``AdminSite``
73
+ # next, then falls back to ``DEFAULT_PRIMARY_COLOR``. Mirrors
74
+ # ``BRAND_TITLE`` / ``BRAND_LOGO_URL``: setting wins as the
75
+ # per-deployment override, AdminSite attr is the structural default,
76
+ # built-in default last (#631).
77
+ "PRIMARY_COLOR": None,
65
78
  # ``REACT_LOGIN`` — React-rendered login is the **default** so the
66
79
  # SPA fully replaces the Django admin URL surface end-to-end (owner
67
80
  # directive 2026-05-28). ``SpaIndexView`` serves the React shell to
@@ -149,7 +162,7 @@ class _PackageSettings:
149
162
  ENABLE_PROFILING: bool = DEFAULTS["ENABLE_PROFILING"]
150
163
  BRAND_TITLE: str | None = DEFAULTS["BRAND_TITLE"]
151
164
  BRAND_LOGO_URL: str | None = DEFAULTS["BRAND_LOGO_URL"]
152
- PRIMARY_COLOR: str = DEFAULTS["PRIMARY_COLOR"]
165
+ PRIMARY_COLOR: str | None = DEFAULTS["PRIMARY_COLOR"]
153
166
  REACT_LOGIN: bool = DEFAULTS["REACT_LOGIN"]
154
167
  API_URL_PREFIX: str | None = DEFAULTS["API_URL_PREFIX"]
155
168
  LEGACY_ADMIN_URL_PREFIX: str | None = DEFAULTS["LEGACY_ADMIN_URL_PREFIX"]
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "../../packages/details/src/JsonViewer.tsx": {
3
- "file": "assets/JsonViewer-CH3aXJXg.js",
3
+ "file": "assets/JsonViewer-DkeJkG12.js",
4
4
  "name": "JsonViewer",
5
5
  "src": "../../packages/details/src/JsonViewer.tsx",
6
6
  "isDynamicEntry": true,
@@ -9,7 +9,7 @@
9
9
  ]
10
10
  },
11
11
  "index.html": {
12
- "file": "assets/index-Bw0i616L.js",
12
+ "file": "assets/index-CGh8t5dj.js",
13
13
  "name": "index",
14
14
  "src": "index.html",
15
15
  "isEntry": true,
@@ -18,11 +18,11 @@
18
18
  "src/ColumnLayoutModal.tsx"
19
19
  ],
20
20
  "css": [
21
- "assets/index-DRQ2gAuA.css"
21
+ "assets/index-CpxUkcdm.css"
22
22
  ]
23
23
  },
24
24
  "src/ColumnLayoutModal.tsx": {
25
- "file": "assets/ColumnLayoutModal-Bo2HCky2.js",
25
+ "file": "assets/ColumnLayoutModal-Bqf6vN1j.js",
26
26
  "name": "ColumnLayoutModal",
27
27
  "src": "src/ColumnLayoutModal.tsx",
28
28
  "isDynamicEntry": true,
@@ -1,4 +1,4 @@
1
- import{c as tt,d as c,R as $,r as ke,j as I,M as kn,b as On,a as Jt}from"./index-Bw0i616L.js";const Tn=[["circle",{cx:"9",cy:"12",r:"1",key:"1vctgf"}],["circle",{cx:"9",cy:"5",r:"1",key:"hp0tcf"}],["circle",{cx:"9",cy:"19",r:"1",key:"fkjjf6"}],["circle",{cx:"15",cy:"12",r:"1",key:"1tmaij"}],["circle",{cx:"15",cy:"5",r:"1",key:"19l28e"}],["circle",{cx:"15",cy:"19",r:"1",key:"f4zoj3"}]],Qt=tt("grip-vertical",Tn);const Ln=[["rect",{width:"18",height:"11",x:"3",y:"11",rx:"2",ry:"2",key:"1w4ew1"}],["path",{d:"M7 11V7a5 5 0 0 1 9.9-1",key:"1mm8w8"}]],jn=tt("lock-open",Ln);const zn=[["rect",{width:"18",height:"11",x:"3",y:"11",rx:"2",ry:"2",key:"1w4ew1"}],["path",{d:"M7 11V7a5 5 0 0 1 10 0v4",key:"fwvmzm"}]],jt=tt("lock",zn);const $n=[["path",{d:"M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8",key:"1357e3"}],["path",{d:"M3 3v5h5",key:"1xhq8a"}]],Bn=tt("rotate-ccw",$n);function Pn(){for(var e=arguments.length,t=new Array(e),n=0;n<e;n++)t[n]=arguments[n];return c.useMemo(()=>r=>{t.forEach(o=>o(r))},t)}const nt=typeof window<"u"&&typeof window.document<"u"&&typeof window.document.createElement<"u";function xe(e){const t=Object.prototype.toString.call(e);return t==="[object Window]"||t==="[object global]"}function mt(e){return"nodeType"in e}function B(e){var t,n;return e?xe(e)?e:mt(e)&&(t=(n=e.ownerDocument)==null?void 0:n.defaultView)!=null?t:window:window}function yt(e){const{Document:t}=B(e);return e instanceof t}function Pe(e){return xe(e)?!1:e instanceof B(e).HTMLElement}function Zt(e){return e instanceof B(e).SVGElement}function we(e){return e?xe(e)?e.document:mt(e)?yt(e)?e:Pe(e)||Zt(e)?e.ownerDocument:document:document:document}const Z=nt?c.useLayoutEffect:c.useEffect;function xt(e){const t=c.useRef(e);return Z(()=>{t.current=e}),c.useCallback(function(){for(var n=arguments.length,r=new Array(n),o=0;o<n;o++)r[o]=arguments[o];return t.current==null?void 0:t.current(...r)},[])}function Fn(){const e=c.useRef(null),t=c.useCallback((r,o)=>{e.current=setInterval(r,o)},[]),n=c.useCallback(()=>{e.current!==null&&(clearInterval(e.current),e.current=null)},[]);return[t,n]}function je(e,t){t===void 0&&(t=[e]);const n=c.useRef(e);return Z(()=>{n.current!==e&&(n.current=e)},t),n}function Fe(e,t){const n=c.useRef();return c.useMemo(()=>{const r=e(n.current);return n.current=r,r},[...t])}function Je(e){const t=xt(e),n=c.useRef(null),r=c.useCallback(o=>{o!==n.current&&t?.(o,n.current),n.current=o},[]);return[n,r]}function ht(e){const t=c.useRef();return c.useEffect(()=>{t.current=e},[e]),t.current}let lt={};function Xe(e,t){return c.useMemo(()=>{if(t)return t;const n=lt[e]==null?0:lt[e]+1;return lt[e]=n,e+"-"+n},[e,t])}function en(e){return function(t){for(var n=arguments.length,r=new Array(n>1?n-1:0),o=1;o<n;o++)r[o-1]=arguments[o];return r.reduce((i,s)=>{const a=Object.entries(s);for(const[l,u]of a){const f=i[l];f!=null&&(i[l]=f+e*u)}return i},{...t})}}const ye=en(1),ze=en(-1);function Xn(e){return"clientX"in e&&"clientY"in e}function wt(e){if(!e)return!1;const{KeyboardEvent:t}=B(e.target);return t&&e instanceof t}function Un(e){if(!e)return!1;const{TouchEvent:t}=B(e.target);return t&&e instanceof t}function gt(e){if(Un(e)){if(e.touches&&e.touches.length){const{clientX:t,clientY:n}=e.touches[0];return{x:t,y:n}}else if(e.changedTouches&&e.changedTouches.length){const{clientX:t,clientY:n}=e.changedTouches[0];return{x:t,y:n}}}return Xn(e)?{x:e.clientX,y:e.clientY}:null}const $e=Object.freeze({Translate:{toString(e){if(!e)return;const{x:t,y:n}=e;return"translate3d("+(t?Math.round(t):0)+"px, "+(n?Math.round(n):0)+"px, 0)"}},Scale:{toString(e){if(!e)return;const{scaleX:t,scaleY:n}=e;return"scaleX("+t+") scaleY("+n+")"}},Transform:{toString(e){if(e)return[$e.Translate.toString(e),$e.Scale.toString(e)].join(" ")}},Transition:{toString(e){let{property:t,duration:n,easing:r}=e;return t+" "+n+"ms "+r}}}),zt="a,frame,iframe,input:not([type=hidden]):not(:disabled),select:not(:disabled),textarea:not(:disabled),button:not(:disabled),*[tabindex]";function Yn(e){return e.matches(zt)?e:e.querySelector(zt)}const Wn={display:"none"};function Vn(e){let{id:t,value:n}=e;return $.createElement("div",{id:t,style:Wn},n)}function Hn(e){let{id:t,announcement:n,ariaLiveType:r="assertive"}=e;const o={position:"fixed",top:0,left:0,width:1,height:1,margin:-1,border:0,padding:0,overflow:"hidden",clip:"rect(0 0 0 0)",clipPath:"inset(100%)",whiteSpace:"nowrap"};return $.createElement("div",{id:t,style:o,role:"status","aria-live":r,"aria-atomic":!0},n)}function Kn(){const[e,t]=c.useState("");return{announce:c.useCallback(r=>{r!=null&&t(r)},[]),announcement:e}}const tn=c.createContext(null);function _n(e){const t=c.useContext(tn);c.useEffect(()=>{if(!t)throw new Error("useDndMonitor must be used within a children of <DndContext>");return t(e)},[e,t])}function qn(){const[e]=c.useState(()=>new Set),t=c.useCallback(r=>(e.add(r),()=>e.delete(r)),[e]);return[c.useCallback(r=>{let{type:o,event:i}=r;e.forEach(s=>{var a;return(a=s[o])==null?void 0:a.call(s,i)})},[e]),t]}const Gn={draggable:`
1
+ import{c as tt,d as c,R as $,r as ke,j as I,M as kn,b as On,a as Jt}from"./index-CGh8t5dj.js";const Tn=[["circle",{cx:"9",cy:"12",r:"1",key:"1vctgf"}],["circle",{cx:"9",cy:"5",r:"1",key:"hp0tcf"}],["circle",{cx:"9",cy:"19",r:"1",key:"fkjjf6"}],["circle",{cx:"15",cy:"12",r:"1",key:"1tmaij"}],["circle",{cx:"15",cy:"5",r:"1",key:"19l28e"}],["circle",{cx:"15",cy:"19",r:"1",key:"f4zoj3"}]],Qt=tt("grip-vertical",Tn);const Ln=[["rect",{width:"18",height:"11",x:"3",y:"11",rx:"2",ry:"2",key:"1w4ew1"}],["path",{d:"M7 11V7a5 5 0 0 1 9.9-1",key:"1mm8w8"}]],jn=tt("lock-open",Ln);const zn=[["rect",{width:"18",height:"11",x:"3",y:"11",rx:"2",ry:"2",key:"1w4ew1"}],["path",{d:"M7 11V7a5 5 0 0 1 10 0v4",key:"fwvmzm"}]],jt=tt("lock",zn);const $n=[["path",{d:"M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8",key:"1357e3"}],["path",{d:"M3 3v5h5",key:"1xhq8a"}]],Bn=tt("rotate-ccw",$n);function Pn(){for(var e=arguments.length,t=new Array(e),n=0;n<e;n++)t[n]=arguments[n];return c.useMemo(()=>r=>{t.forEach(o=>o(r))},t)}const nt=typeof window<"u"&&typeof window.document<"u"&&typeof window.document.createElement<"u";function xe(e){const t=Object.prototype.toString.call(e);return t==="[object Window]"||t==="[object global]"}function mt(e){return"nodeType"in e}function B(e){var t,n;return e?xe(e)?e:mt(e)&&(t=(n=e.ownerDocument)==null?void 0:n.defaultView)!=null?t:window:window}function yt(e){const{Document:t}=B(e);return e instanceof t}function Pe(e){return xe(e)?!1:e instanceof B(e).HTMLElement}function Zt(e){return e instanceof B(e).SVGElement}function we(e){return e?xe(e)?e.document:mt(e)?yt(e)?e:Pe(e)||Zt(e)?e.ownerDocument:document:document:document}const Z=nt?c.useLayoutEffect:c.useEffect;function xt(e){const t=c.useRef(e);return Z(()=>{t.current=e}),c.useCallback(function(){for(var n=arguments.length,r=new Array(n),o=0;o<n;o++)r[o]=arguments[o];return t.current==null?void 0:t.current(...r)},[])}function Fn(){const e=c.useRef(null),t=c.useCallback((r,o)=>{e.current=setInterval(r,o)},[]),n=c.useCallback(()=>{e.current!==null&&(clearInterval(e.current),e.current=null)},[]);return[t,n]}function je(e,t){t===void 0&&(t=[e]);const n=c.useRef(e);return Z(()=>{n.current!==e&&(n.current=e)},t),n}function Fe(e,t){const n=c.useRef();return c.useMemo(()=>{const r=e(n.current);return n.current=r,r},[...t])}function Je(e){const t=xt(e),n=c.useRef(null),r=c.useCallback(o=>{o!==n.current&&t?.(o,n.current),n.current=o},[]);return[n,r]}function ht(e){const t=c.useRef();return c.useEffect(()=>{t.current=e},[e]),t.current}let lt={};function Xe(e,t){return c.useMemo(()=>{if(t)return t;const n=lt[e]==null?0:lt[e]+1;return lt[e]=n,e+"-"+n},[e,t])}function en(e){return function(t){for(var n=arguments.length,r=new Array(n>1?n-1:0),o=1;o<n;o++)r[o-1]=arguments[o];return r.reduce((i,s)=>{const a=Object.entries(s);for(const[l,u]of a){const f=i[l];f!=null&&(i[l]=f+e*u)}return i},{...t})}}const ye=en(1),ze=en(-1);function Xn(e){return"clientX"in e&&"clientY"in e}function wt(e){if(!e)return!1;const{KeyboardEvent:t}=B(e.target);return t&&e instanceof t}function Un(e){if(!e)return!1;const{TouchEvent:t}=B(e.target);return t&&e instanceof t}function gt(e){if(Un(e)){if(e.touches&&e.touches.length){const{clientX:t,clientY:n}=e.touches[0];return{x:t,y:n}}else if(e.changedTouches&&e.changedTouches.length){const{clientX:t,clientY:n}=e.changedTouches[0];return{x:t,y:n}}}return Xn(e)?{x:e.clientX,y:e.clientY}:null}const $e=Object.freeze({Translate:{toString(e){if(!e)return;const{x:t,y:n}=e;return"translate3d("+(t?Math.round(t):0)+"px, "+(n?Math.round(n):0)+"px, 0)"}},Scale:{toString(e){if(!e)return;const{scaleX:t,scaleY:n}=e;return"scaleX("+t+") scaleY("+n+")"}},Transform:{toString(e){if(e)return[$e.Translate.toString(e),$e.Scale.toString(e)].join(" ")}},Transition:{toString(e){let{property:t,duration:n,easing:r}=e;return t+" "+n+"ms "+r}}}),zt="a,frame,iframe,input:not([type=hidden]):not(:disabled),select:not(:disabled),textarea:not(:disabled),button:not(:disabled),*[tabindex]";function Yn(e){return e.matches(zt)?e:e.querySelector(zt)}const Wn={display:"none"};function Vn(e){let{id:t,value:n}=e;return $.createElement("div",{id:t,style:Wn},n)}function Hn(e){let{id:t,announcement:n,ariaLiveType:r="assertive"}=e;const o={position:"fixed",top:0,left:0,width:1,height:1,margin:-1,border:0,padding:0,overflow:"hidden",clip:"rect(0 0 0 0)",clipPath:"inset(100%)",whiteSpace:"nowrap"};return $.createElement("div",{id:t,style:o,role:"status","aria-live":r,"aria-atomic":!0},n)}function Kn(){const[e,t]=c.useState("");return{announce:c.useCallback(r=>{r!=null&&t(r)},[]),announcement:e}}const tn=c.createContext(null);function _n(e){const t=c.useContext(tn);c.useEffect(()=>{if(!t)throw new Error("useDndMonitor must be used within a children of <DndContext>");return t(e)},[e,t])}function qn(){const[e]=c.useState(()=>new Set),t=c.useCallback(r=>(e.add(r),()=>e.delete(r)),[e]);return[c.useCallback(r=>{let{type:o,event:i}=r;e.forEach(s=>{var a;return(a=s[o])==null?void 0:a.call(s,i)})},[e]),t]}const Gn={draggable:`
2
2
  To pick up a draggable item, press the space bar.
3
3
  While dragging, use the arrow keys to move the item.
4
4
  Press space again to drop the item in its new position, or press escape to cancel.
@@ -1 +1 @@
1
- import{c as d,d as l,j as e,C as h}from"./index-Bw0i616L.js";const p=[["path",{d:"m9 18 6-6-6-6",key:"mthhwq"}]],g=d("chevron-right",p);const y=[["rect",{width:"14",height:"14",x:"8",y:"8",rx:"2",ry:"2",key:"17jyea"}],["path",{d:"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2",key:"zix9uf"}]],j=d("copy",y);function N({raw:t,parsed:r}){const[s,c]=l.useState(!1);async function a(){try{await navigator.clipboard.writeText(t),c(!0),setTimeout(()=>c(!1),2e3)}catch{}}return e.jsxs("div",{className:"relative w-full overflow-x-auto rounded border border-gray-200 bg-gray-50 p-3 font-mono text-xs leading-relaxed text-gray-800 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-200",children:[e.jsx("button",{type:"button",onClick:a,"aria-label":"Copy JSON",title:s?"Copied":"Copy",className:"absolute right-2 top-2 inline-flex h-6 w-6 items-center justify-center rounded border border-gray-300 bg-white text-gray-600 hover:bg-gray-100 dark:border-gray-600 dark:bg-gray-800 dark:text-gray-300 dark:hover:bg-gray-700",children:s?e.jsx(h,{className:"h-3.5 w-3.5 text-green-600","aria-hidden":!0}):e.jsx(j,{className:"h-3.5 w-3.5","aria-hidden":!0})}),e.jsx(o,{value:r,depth:0})]})}function o({value:t,depth:r}){return t===null?e.jsx("span",{className:"text-purple-600 dark:text-purple-400",children:"null"}):typeof t=="boolean"?e.jsx("span",{className:"text-purple-600 dark:text-purple-400",children:t?"true":"false"}):typeof t=="number"?e.jsx("span",{className:"text-blue-700",children:String(t)}):typeof t=="string"?e.jsxs("span",{className:"text-green-700 dark:text-green-400",children:['"',t,'"']}):Array.isArray(t)?e.jsx(u,{value:t,depth:r}):typeof t=="object"?e.jsx(m,{value:t,depth:r}):e.jsx("span",{className:"text-gray-500",children:String(t)})}function m({value:t,depth:r}){const s=Object.keys(t),[c,a]=l.useState(r<2);return s.length===0?e.jsx("span",{className:"text-gray-500",children:"{}"}):e.jsx(x,{open:c,onToggle:()=>a(n=>!n),collapsedLabel:`{…} ${s.length} ${s.length===1?"key":"keys"}`,openBracket:"{",closeBracket:"}",depth:r,children:s.map((n,i)=>e.jsxs("div",{className:"pl-4",children:[e.jsxs("span",{className:"text-rose-700 dark:text-rose-400",children:['"',n,'"']}),e.jsx("span",{className:"text-gray-500",children:": "}),e.jsx(o,{value:t[n],depth:r+1}),i<s.length-1?e.jsx("span",{className:"text-gray-500",children:","}):null]},n))})}function u({value:t,depth:r}){const[s,c]=l.useState(r<2);return t.length===0?e.jsx("span",{className:"text-gray-500",children:"[]"}):e.jsx(x,{open:s,onToggle:()=>c(a=>!a),collapsedLabel:`[…] ${t.length} ${t.length===1?"item":"items"}`,openBracket:"[",closeBracket:"]",depth:r,children:t.map((a,n)=>e.jsxs("div",{className:"pl-4",children:[e.jsx(o,{value:a,depth:r+1}),n<t.length-1?e.jsx("span",{className:"text-gray-500",children:","}):null]},n))})}function x({open:t,onToggle:r,collapsedLabel:s,openBracket:c,closeBracket:a,depth:n,children:i}){return e.jsxs("span",{children:[e.jsx("button",{type:"button",onClick:r,"aria-expanded":t,className:"inline-flex items-center align-baseline text-gray-500 hover:text-gray-800 dark:hover:text-gray-200",children:e.jsx(g,{className:`h-3 w-3 shrink-0 transition-transform ${t?"rotate-90":""}`,"aria-hidden":!0})}),e.jsx("span",{className:"text-gray-500",children:c}),t?e.jsxs(e.Fragment,{children:[i,e.jsx("div",{className:n===0?"":"pl-0",children:e.jsx("span",{className:"text-gray-500",children:a})})]}):e.jsxs(e.Fragment,{children:[e.jsx("span",{className:"px-1 text-gray-500",children:s}),e.jsx("span",{className:"text-gray-500",children:a})]})]})}export{N as default};
1
+ import{c as d,d as l,j as e,C as h}from"./index-CGh8t5dj.js";const p=[["path",{d:"m9 18 6-6-6-6",key:"mthhwq"}]],g=d("chevron-right",p);const y=[["rect",{width:"14",height:"14",x:"8",y:"8",rx:"2",ry:"2",key:"17jyea"}],["path",{d:"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2",key:"zix9uf"}]],j=d("copy",y);function N({raw:t,parsed:r}){const[s,c]=l.useState(!1);async function a(){try{await navigator.clipboard.writeText(t),c(!0),setTimeout(()=>c(!1),2e3)}catch{}}return e.jsxs("div",{className:"relative w-full overflow-x-auto rounded border border-gray-200 bg-gray-50 p-3 font-mono text-xs leading-relaxed text-gray-800 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-200",children:[e.jsx("button",{type:"button",onClick:a,"aria-label":"Copy JSON",title:s?"Copied":"Copy",className:"absolute right-2 top-2 inline-flex h-6 w-6 items-center justify-center rounded border border-gray-300 bg-white text-gray-600 hover:bg-gray-100 dark:border-gray-600 dark:bg-gray-800 dark:text-gray-300 dark:hover:bg-gray-700",children:s?e.jsx(h,{className:"h-3.5 w-3.5 text-green-600","aria-hidden":!0}):e.jsx(j,{className:"h-3.5 w-3.5","aria-hidden":!0})}),e.jsx(o,{value:r,depth:0})]})}function o({value:t,depth:r}){return t===null?e.jsx("span",{className:"text-purple-600 dark:text-purple-400",children:"null"}):typeof t=="boolean"?e.jsx("span",{className:"text-purple-600 dark:text-purple-400",children:t?"true":"false"}):typeof t=="number"?e.jsx("span",{className:"text-blue-700",children:String(t)}):typeof t=="string"?e.jsxs("span",{className:"text-green-700 dark:text-green-400",children:['"',t,'"']}):Array.isArray(t)?e.jsx(u,{value:t,depth:r}):typeof t=="object"?e.jsx(m,{value:t,depth:r}):e.jsx("span",{className:"text-gray-500",children:String(t)})}function m({value:t,depth:r}){const s=Object.keys(t),[c,a]=l.useState(r<2);return s.length===0?e.jsx("span",{className:"text-gray-500",children:"{}"}):e.jsx(x,{open:c,onToggle:()=>a(n=>!n),collapsedLabel:`{…} ${s.length} ${s.length===1?"key":"keys"}`,openBracket:"{",closeBracket:"}",depth:r,children:s.map((n,i)=>e.jsxs("div",{className:"pl-4",children:[e.jsxs("span",{className:"text-rose-700 dark:text-rose-400",children:['"',n,'"']}),e.jsx("span",{className:"text-gray-500",children:": "}),e.jsx(o,{value:t[n],depth:r+1}),i<s.length-1?e.jsx("span",{className:"text-gray-500",children:","}):null]},n))})}function u({value:t,depth:r}){const[s,c]=l.useState(r<2);return t.length===0?e.jsx("span",{className:"text-gray-500",children:"[]"}):e.jsx(x,{open:s,onToggle:()=>c(a=>!a),collapsedLabel:`[…] ${t.length} ${t.length===1?"item":"items"}`,openBracket:"[",closeBracket:"]",depth:r,children:t.map((a,n)=>e.jsxs("div",{className:"pl-4",children:[e.jsx(o,{value:a,depth:r+1}),n<t.length-1?e.jsx("span",{className:"text-gray-500",children:","}):null]},n))})}function x({open:t,onToggle:r,collapsedLabel:s,openBracket:c,closeBracket:a,depth:n,children:i}){return e.jsxs("span",{children:[e.jsx("button",{type:"button",onClick:r,"aria-expanded":t,className:"inline-flex items-center align-baseline text-gray-500 hover:text-gray-800 dark:hover:text-gray-200",children:e.jsx(g,{className:`h-3 w-3 shrink-0 transition-transform ${t?"rotate-90":""}`,"aria-hidden":!0})}),e.jsx("span",{className:"text-gray-500",children:c}),t?e.jsxs(e.Fragment,{children:[i,e.jsx("div",{className:n===0?"":"pl-0",children:e.jsx("span",{className:"text-gray-500",children:a})})]}):e.jsxs(e.Fragment,{children:[e.jsx("span",{className:"px-1 text-gray-500",children:s}),e.jsx("span",{className:"text-gray-500",children:a})]})]})}export{N as default};