Frappe's tab model does not know subtabs exist. Tab.set_active() strips `show active` off every pane except the one it activates, and Frappe calls it on every refresh_sections() — which runs on every refresh_field() and every value change. So editing any field wiped the open subtab: its content appeared for a fraction of a second and vanished. Clicking a subtab now registers its top-level ancestor as the active tab, so Frappe's own bookkeeping stays coherent and keeps re-activating the right tab, and Tab.set_active is wrapped to re-assert the rest of the chain once Frappe is done. The whole ancestor chain has to be restored, not just the leaf: subtabs nest two deep in several declarations, where the top pane holds the first subtab bar, the middle pane holds the second and the leaf holds the content — three panes active at once, which Frappe's one-tab-at-a-time model will never do on its own. This replaces the MutationObserver, which was fighting the same battle from the wrong end: it could not tell a genuine tab switch from a refresh re-asserting the tab already open. Verified: 70 subtabs across 11 doctypes stay visible through refresh_field() and refresh_sections(). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|---|---|---|
| custom_subtabs | ||
| .gitignore | ||
| ARCHITECTURE.md | ||
| README.md | ||
| license.txt | ||
| pyproject.toml | ||
README.md
Custom Subtabs
Custom subtabs
License
unlicense