| status | in-force |
|---|---|
| summary | What a tool must do to show a tab in FNS_Hub -- the FNS_HubRegistry host, RegisterTab, the three tab kinds, exposure, and the hazards the build paid for. |
| since | 2026-08-23 (FNS_Hub build, branch uniUI) |
| verified | 2026-08-23 -- Toolbar/Navbar/MainMenu configurators and the console viewer are the first four tabs; late registration and drop-to-register verified live |
| skill | fns-registry |
What a tool must do to appear as a tab in the toolkit's one-stop manager
window. Source of truth:
HubRegistryExt.py (RegisterTab,
_validateTab, _syncSurface) and HubExt.py
(the window side). The registry pattern this sits on is
RegistryScheme.md; the promoted global lives per
RegistryHomeContract.md. The console's web tabs are a
different surface with their own contract (ConsoleTabContract.md);
the console itself is one hub tab.
FNSTools/FNS_Hub is a core package: the FNS button in TD's main-menu bar
(a widget published through FNS_MainMenuRegistry as canonical FNS) and a
window (panel: a tab bar on top and a tabs container below). The bar has
two styles, the hub's Tab Bar par (Tabbar: rows | strip, roams):
rows is the hub-owned tabbar -- one textCOMP per shown tab copied from
tab_template, laid out by expressions so it wraps into rows, active =
brighter label + accent underline, drag a tab onto another to reorder
(dragdrop_callbacks; a textCOMP needs dragdropmode=panel for panel
drag/drop -- its default textinsert is text-field semantics); strip is TD's masterFolderTabs widget -- one row, scroll
arrows, dropdown, the widget's own drag-to-reorder. Both land on
OnTabReorder and the roaming Tab User Order par. Neither has a close
button: hidden tabs come back through the right-click menu. The hub
holds no tab knowledge. FNS_HubRegistry -- the eighth registry, promoted to
/sys/FNS_Registries, shortcut op.FNS_HUBREGISTRY -- owns the entries, and
its _syncSurface injects one rendering op per entry into panel/tabs
(hubtab_<canonical>), prunes the stale ones, and calls HubExt.RefreshTabs().
The hub only decides which hubtab_* is displayed and keeps the folder-tab bar
in step.
A tab is a contribution like every other surface: the tool carries a
stamped FNS_HubRegistry host whose Registration pars say what the tab shows.
Nothing is discovered by scanning -- the host registers at its own init, the
base healing tick re-asks unpublished hosts in the boot window, and a host
stamped while the hub is open appears without any rescan (verified).
| Registration pars | Kind | Rendered as |
|---|---|---|
Tab Content empty, Tool COMP is a panel |
panel |
a Select COMP mirroring the tool -- it can live anywhere in the project |
Tab Content = a panel COMP inside the tool |
panel |
a Select COMP mirroring that panel |
Tab Content = a DAT / CHOP / TOP / SOP / POP / MAT |
opviewer |
an OP Viewer COMP (interactive) |
Tab Parameters = a page scope (* = all) |
params |
a Parameter COMP on the tool, built-in pages off |
Tab Parameters wins over Tab Content. Tab Label defaults to the canonical
name; Tab Order defaults to 50 (the configurators sit at 10/20/30/40, the
Overlay tab at 50, the console at 90); Shown in Hub (Displayed) is what the hub's close button
writes back to, so a hidden tab stays hidden across projects through the
tool's Registry page.
op.FNS_HUBREGISTRY.RegisterTab(comp, canonical, content=None, params='', label='',
order=50, displayed=True, help_url='', source_registry=None)Guarded, like every registry; a host that is not the /sys global forwards.
UnregisterTab(canonical) (aliased UnregisterPanel for RegistryBase's host
teardown), Tabs(include_hidden=False), SetTabDisplayed(canonical, bool),
Open(tab=None), OpenDocs(canonical) are the rest. Rejected (debug line, no
exception): no tool COMP; empty canonical; canonical not letters/digits/_/-;
content neither a panel nor a viewable operator; content gone.
StampHost is the one way to put a host in a tool. par_values may carry
Comp/Tabcontent -- that is how the hub itself publishes the console tab
(FNS_Hub/FNS_HubRegistry, Comp='../webBrowser'), because the root's
webBrowser is a clone master (see §6).
Only the shown tab's component is "live". When a tab is shown or hidden the hub
calls OnHubExposure(exposed) on the component's extension if it defines one;
otherwise, a component with Active and Address pars (a palette Web Browser)
has its Active switched directly. A tool whose own visibility rule
governs a renderer must implement the hook -- a palette browser nested
inside a tool is invisible to the hub's fallback, and the tool's own watchers
(Render Only While Viewer Active) or the console host's "local browser off
while served" rule would otherwise keep it dark in the hub. ColorUI is the
worked example: ExtColorUI.OnHubExposure feeds _hubExposed into its one
SyncLocalBrowser rule (hub shown OR (viewer open AND not served)). A component carrying a Refresh pulse par
is pulsed each time its tab is shown (the old tools_ui convention). Right-click
on a tab opens the owning tool's parameter window
(HubExt.OpenTabParameters(index)).
A mirrored root panel has no panel parent, so it must have a fixed size:
the tool tabs bind w/h to op.FNS.op('FNS_Hub/panel/tabs') with their old
standalone size as the fallback when no hub exists. Every tab is un-exposed when the window
closes: a windowCOMP has no close callback and the panel's winopen value
stays 0 under a windowCOMP, so HubExt runs a once-a-second isOpen check
only while the window is open (_armCloseWatch). The root webBrowser's
own watchers (Render Only While Window Open / Viewer Active) are OFF: with
either on, its per-frame sync() fights the hub.
panel/drop_callbacks serves both the hub window and select1 (the FNS
button). A Select mirror does NOT forward drops by itself -- the mirror's
own Drag/Drop pars decide, and registry-made mirrors defaulted to the bar's
legacy dropparent; RegistryBase._mirrorDragDrop now copies a source
widget's callback-mode settings onto its mirror on every inject (MainMenu and
Toolbar registries), which is what makes the FNS button a drop target in the
bar (verified by hand 2026-08-23). Nothing runs
in the drop-event stack: RouteDrop records paths and defers a frame; every
tab whose component promotes AcceptsDrop + PackageDrop is a target; one
target stamps directly, several open a popMenu; the configurator's
_stampPackage then calls the registry master's StampHost. A COMP that
belongs on two surfaces is dropped twice.
- Never stamp a host into a clone master.
/FNSTools/webBrowseris cloned byColorUI/webBrowser; a host stamped into the master replicated into the clone, and the clone's copy won the registration. Hosts for shared chrome live in the hub and point at the content by path. - The
/sysglobal runs a COPY of the extension DAT with no file sync. EditingHubRegistryExt.pyreaches the master only; push the master's DAT text into the global and reinit (or re-promote) to test. - Folder-tab labels cannot contain spaces --
masterFolderTabssplitsMenulabelson whitespace, soMain Menubecame two tabs. UseMainMenu. - A retry loop that gives up must clear its queued flag --
_syncSurfaceleft_pane_sync_queuedTrue after exhausting its attempts and nothing could ever re-arm the sync. - OP-reference par values resolve sibling-relative for a COMP owner
(
Comp='select1', not'../select1'), while'..'still means the parent. - The old
_stampPackageaddressed the host by its pre-v3 name -- the configurators' drop-to-register had been dead since theFNS_rename; the port toStampHostfixed it and removed the dormant template hosts.
Since 2026-08-23 every configurator tab is a thin subclass of
FNSTools/FNS_Hub/ConfiguratorBase.py
(a DAT next to the configurators; subclasses import it with
mod('../ConfiguratorBase')). A new surface configurator is a class-attribute
description (registry shortcut/ext/name, bar path, HAS_SIDES / HAS_KINDS /
HAS_DIVIDERS / HAS_WIDTH / HAS_GROUPS / SUPPORTS_DROP /
ADOPTS_BUILTINS, adoption flags, STAMP_EXTRA_PARS) plus the adoption
hooks -- the registry API it expects is the RegistryBase widget model
(Widgets, WidgetSequence, SetWidgetSequence, SetWidgetDisplay,
groups). A registry with a different model (the OP menu's contributions)
adapts itself to that shape rather than forking the base.
The three configurators are PI suspects of their own
(modules/suspects/FNSTools/FNS_Hub/<Name>Configurator.tox, enableexternaltox
on) nested under the hub's tox -- individually tracked and releasable.
Their ConfiguratorExt DATs are live, so that tox is the sole carrier of
the code. Save order: configurator toxes, then FNS_Hub, then the root.
A released configurator is a hub contribution (its FNS_HubRegistry host
stays live in the artifact, cloning off); dropped into a project with the
hub it registers its tab, alone it has no window.
/sys is rebuilt every open: hosts re-register on init. The hub's tab order
and active tab roam as plain pars on its Hub page (config host canonical
FNS_Hub, About page excluded); the configurators' state tables roam
through the hub's config_callbacks, keyed by configurator name.