Docs Core

Console

The toolkit's web front, served from inside your project: every tool's settings on one page, the install picker, and any tab a tool contributes. The raw master, promoted to /sys.

◈ Core

The console is the one place to look at and steer an installed toolkit from a browser: every tool's settings on a single scrollable page, the install picker, and any tab a tool decides to contribute. It is served from inside your project on 127.0.0.1, only while you are looking at it. It routes every request to the component that owns the answer, so all tool knowledge stays with the tools.

Opening it

From the toolkit root's FNSTools parameter page:

Pulse Lands on
Open Settings the Settings tab
Pick Tools the Install & remove tab

From the FNS_Console component itself: Open Console. From Python:

op.FNS_CONSOLE.Open()                 # Settings
op.FNS_CONSOLE.Open(tab='tools')      # Install & remove
op.FNS_CONSOLE.Open(tab='ColorUI')    # a contributed tab, by canonical name
op.FNS_CONSOLE.Open(panel=False)      # force the system browser

It shows in the toolkit root's webBrowser panel when the root has one (the bootstrap ships one), else in your system browser. The panel handles everything the page does, file dialogs included. The tab is the URL fragment (#settings, #tools, #t-<name>), so a reload keeps it and a link can deep-link to it.

The tabs

  • Settings: every registered tool as a section card (sticky header, parameters grouped by page), a jump list with scroll-spy on the left, search across all tools. Writing a value goes through FNS_ConfigRegistry's own filters and persistence, so the page can never disagree with the components. The header carries scope (global/project; see ConfigRegistry), Export (writes <config dir>/exports/FNStools_config_<stamp>.json server-side and offers a download) and Import (applies a document to installed tools now and keeps sections for tools you install later).
  • Install & remove: the FNS_Installer picker, served through the console. Unchecking an installed tool removes it; its settings are kept for the next install. Installing decides what is in the project; the config layer always re-applies on top. In the toolkit's own source checkout this tab is locked (it would remove authored masters).
  • Contributed tabs: anything a tool registers (below). They sit after a divider, sorted by their declared order; the two built-ins are the console's own and always shown.

Exposure: shipped dormant, enabled by the install

Every tool artifact ships with Expose to Console off, and the toolkit's installer turns it on as it lands the package. One artifact therefore serves two lives:

How the tool arrived Console exposure Its own panel
dropped into a project on its own (a standalone plugin) off; nothing raises a console works as always
installed by FNS_Installer (bootstrap, picker, Textport rail) on, flipped once at install off while exposed; the console is the UI
updated by FNS_Updater untouched; your choice persists follows your choice

Why it ships off: a registry host bootstraps its own /sys global when none exists, which is right for a toolbar button (it adds a capability) and wrong for the console, whose exposure removes a local surface. A standalone ColorUI shipped exposed would raise a console nobody asked for and switch off its own panel. The rule, for any future surface with that property: a host whose exposure takes a local surface away ships dormant, and only the install rail enables it.

The flag is the tool's own Expose to Console parameter (its Registry page), which the config registry persists, so after the install's one decision, your choice roams across projects and survives updates. If you drag a toolkit package into a project by hand instead of installing it, it arrives in local mode: flip Expose on the tool's page.

Managing tabs

The ⋯ button at the end of the tab bar lists every contributed tab, hidden ones included, with a show/hide checkbox. The choice is written back to the contributing tool's Shown in Console parameter, so it persists with the tool and roams with its Registry page like any other setting; the console keeps no list of its own. A tool decides its own default the same way, and can stay out of the console entirely (below).

How it runs

FNS_Console is a registry in the FNS sense (it sits on RegistryBase like the toolbar, navbar and config registries):

  • The in-project master FNSTools/FNS_Console promotes a global copy to /sys/FNS_Registries/FNS_Console, reachable as op.FNS_CONSOLE. The global is the API owner; the master and every host forward to it.
  • The surface is the server. The global owns a Web Server DAT (console_server) that is created on demand, bound to the first free port in 36710-36759, switched on by Open, and switched off after ten idle minutes. It is never saved into the .toe or any package, since /sys is rebuilt on every project open. Several open projects each run their own console on their own port.
  • The page (console_page) and the request dispatcher (console_server_callbacks) ride along from the master; the global pulls them from the master if a promoted copy predates them.
  • Routing: /api/* goes to FNS_ConfigRegistry's Ui* methods (the paths are the registry's original ones; the TDXLPP launcher reads /api/state and /api/set); /tools and the picker's endpoints go to FNS_Installer.ServeRequest; /t/<tab>/... goes to the tool that contributed the tab. Without the config package the Settings tab says so; without an installer the Install tab says so.
  • Everything is 127.0.0.1 only.
  • The root's webBrowser panel (where the console and the picker show in TD) renders only while someone can see it: the component's own Render Only While Window Open / Render Only While Viewer Active watchers follow the floating window and the node's Viewer Active flag, and the console and installer switch it on as they open it. Closed, its Web Render cooks nothing.

Contributing a tab

A tool contributes a tab the same way it contributes a toolbar button or its settings: it carries a stamped FNS_Console host. The host's Registration page:

Par Meaning
Tool COMP the contributing tool; defaults to the parent (the host lives inside the tool)
Canonical Name URL-safe (letters, digits, _ -); the tab lives at /t/<name>/. Empty = the tool's name
Tab Page a text DAT holding the tab's HTML. Served verbatim in an iframe under /t/<name>/
Tab API optional Python DAT defining onConsoleRequest(action, method, body)
Local Browser optional: the tool's own web browser panel rendering the same page. While the tab is exposed the console serves the page and switches that browser's Active off; a renderer nobody looks at would keep a CEF process and a texture alive. It switches Active back on when exposure ends (Expose off, a failed registration, the host removed). A tool that wants finer control implements OnConsoleExposure(exposed) on its extension; the host then calls that and leaves Active alone
Expose to Console (Auto-register) the tool's own decision: on = publish the tab while the component exists; off = local only; the tool keeps its own interface and contributes nothing
Register / Status publish once regardless of Expose; read-only status
Shown in Console (Displayed) on the bar or hidden. The console's tab manager writes here, so a user's show/hide persists with the tool
Tab Label / Tab Order what the tab bar shows, and where. Built-ins sit at 0 and 10; contributions default to 50 and sort by order, then label

These are mirrored onto the tool's own Registry page (prefix Cs), so a tool like ColorUI exposes, hides or goes local-only from its own parameters without anyone opening the host.

Two rules that follow from how it is served:

  1. The page is self-contained. Inline its CSS and JS; there is no asset route. Relative URLs resolve under /t/<name>/, so fetch('api/x') reaches your API; a root-relative /api/... would reach the console's API instead.
  2. DAT parameters take sibling names. An OP parameter resolves from the host's parent network, i.e. the tool. Write webui_html, not ../webui_html (that looks one level too high).

The API DAT's single function:

def onConsoleRequest(action, method, body):
    """action = the path after /t/<name>/api/, method = 'GET'|'POST',
    body = parsed JSON of a POST, else None. Return anything JSON-able."""

What a tool contributes is a web re-expression of what it owns: tables, parameters, state, talking to the same extension its panel talks to. A TouchDesigner panel itself cannot be embedded in a browser.

Stamping the host from Python (the master does the copy safely):

op.FNS.op('FNS_Console').ext.ConsoleRegistryExt.StampHost(
    op.FNS.op('MyTool'), canonical_name='MyTool', autoregister=True,
    par_values={'Tabpage': 'my_page', 'Tabapi': 'my_api',
                'Tablabel': 'My Tool', 'Taborder': 30})

Or register directly, without a host, from your own extension:

op.FNS_CONSOLE.RegisterTab(comp, 'MyTool', page=op('my_page'),
                           api_dat=op('my_api'), label='My Tool', order=30)

A direct registration is ephemeral (the global is rebuilt every open); re-register on your onStart. A host re-publishes itself.

Worked example: ColorUI

ColorUI's panel already is a web page (webui_html, rendered in its own Web Render browser, talking to TD through document.title rewrites up and executeJavaScript down). Its console tab is the same page with a second transport, picked at load:

const SERVED = /^https?:$/.test(location.protocol);
const Bridge = SERVED ? HttpBridge : TitleBridge;   // same post()/ack() surface

Served, HttpBridge POSTs every command to api/cmd and gets the fresh state back, and polls api/state every two seconds so edits made in the in-TD panel show up. The extension side is two methods over the code the panel already used:

def ConsoleState(self):
    return {'ok': True, 'state': self._statePayload()}

def ConsoleCommand(self, msg):
    self._toast_sink = []            # toasts a command raises ride back
    try:
        self._dispatch(msg)          # the title bridge's vocabulary
    finally:
        toasts, self._toast_sink = self._toast_sink, None
    return {'ok': True, 'state': self._statePayload(), 'toasts': toasts}

and the whole console_api DAT:

def onConsoleRequest(action, method, body):
    ext = parent.ColorUI.ext.ExtColorUI
    if action == 'state':
        return ext.ConsoleState()
    if action == 'cmd' and method == 'POST':
        return ext.ConsoleCommand(body)
    return {'ok': False, 'why': 'unknown action: %s %s' % (method, action)}

Host pars: Tab Page webui_html, Tab API console_api, Local Browser webBrowser, label ColorUI, order 20.

ColorUI goes one step further and keeps its renderer off whenever nobody can see it: a Panel Execute DAT (watch_viewer) on the panel value winopen ("1 if panel is open as a floating window") feeds SyncLocalBrowser, whose single rule is Active = viewer open AND not served by the console. The console's hand-off (OnConsoleExposure) and the Expose parameter call the same rule, so there is one owner and no flicker. Consequences: with the viewer closed the Web Render cooks nothing at all; Open UI opens the console's tab while served, else the panel (and the renderer with it); JavaScript pushes and reload-kicks are skipped while the renderer is off.

HTTP API

All on the console's port, 127.0.0.1 only.

Route Method Answers with
/ GET the console page
/api/tabs GET {ok, tabs:[{name, label, order, builtin, displayed, url?, api?, source?}]}; hidden contributions included
/api/tabs/display POST {name, displayed} {ok, name, displayed, persisted}; built-ins refuse
/api/state GET every registered tool with its exposed parameters (+ scope, project, installer)
/api/set POST {tool, par, value} {ok, val}, validated against what /api/state exposes
/api/export GET {ok, document, tools, saved_to}; the document is also written to <config dir>/exports/
/api/import POST (a config document) {ok, applied:[...], deferred:[...], scope}
/api/scope GET / POST {value, mode} {ok, scope}; global requires mode push or adopt
/tools, /manifest.js, /selection, /status, /install any forwarded to FNS_Installer
/t/<name>/ GET the contributed tab's page
/t/<name>/api/<action> GET / POST the tab's onConsoleRequest

Python API

On op.FNS_CONSOLE (any host or the master forwards):

Call Does
Open(tab=None, panel=True) serve + show; returns {ok, url, shown}
Close() stop serving now
Url() the URL while serving, else None
Tabs(include_hidden=False) the ordered tab list; hidden contributions only when asked
SetTabDisplayed(name, bool) show/hide a contribution, written back to its host's Displayed par
RegisterTab(comp, name, page=, api_dat=, label=, order=, displayed=True) / UnregisterTab(name) contributions without a host
ServeTab(name, subpath, method, body) what the dispatcher calls for /t/...

op.FNS_CONFIGREGISTRY.OpenSettingsUI(tab, panel) still works; it forwards here.

Developing on it

  • Sources are Embody-externalized under FNSTools/FNS_Console/ (ConsoleRegistryExt.py, console_server_callbacks.py, console_page.html, pre_release.py). A disk edit hot-syncs into the master and into the /sys global (its DATs keep the file binding), so the running console picks it up on the next request; reload the page.
  • Structural changes under FNSTools (a new host, a new DAT) must be persisted the project's way or a reload reverts them: pi.Add for new components, pi.Save on the changed suspects, pi.Save(op.FNS) last, then save the project.
  • Promoted globals live in /sys/FNS_Registries; a findChildren from / does not descend there; search that container explicitly.
  • The pre-release hook scrubs registration state, the shortcut, cloning, the external-tox binding and any server DAT, so a shipped FNS_Console is inert until it promotes in the user's project.

Troubleshooting

Symptom Cause / fix
"no free port in 36710-36759" fifty consoles open, or that block is reserved on the machine (netsh interface ipv4 show excludedportrange protocol=tcp); close some, or change UI_PORTS
Page stops answering after ~10 minutes by design: the server idles out; pulse Open again
Settings tab says the config package is missing FNS_ConfigRegistry is not installed or not yet promoted
Install tab says there is no installer the project has no FNS_Installer; drop the bootstrap or the bare installer
Install tab shows Locked (source checkout) you are in the toolkit's own source root; point Install Into at a scratch container
A contributed tab is missing host status shows why (Registration Status): no page DAT, a name that is not URL-safe, or a name clashing with a built-in. Remember sibling names for the DAT pars
A tab's fetch('/api/...') hits the wrong thing root-relative paths reach the console; use relative api/...
Register pulse "did nothing" in a script parameter callbacks fire at frame end; read the status on the next frame

Get it

Tick it on the install page and it installs with the rest of your setup and stays up to date.

Download FNS_Console.tox Version 3.2.17, the tool on its own. It works in any project, and this copy never updates itself.

Parameters

Registration

ControlType / defaultWhat it does
Tool COMPComp operator.. The tool that contributes this tab. Defaults to the parent component (the host lives inside the tool it publishes).
Canonical NameCanonicalname textempty Unique, URL-safe name of the tab (letters, digits, _ -). It is the path: /t//. Leave empty to use the tool COMP name.
Tab PageTabpage operatorempty Text DAT holding the tab's HTML. Served verbatim in the console under /t//. Inline its CSS and JS; relative fetches resolve under that path.
Tab APITabapi operatorempty Optional Python DAT defining onConsoleRequest(action, method, body) -> JSON-able. Answers /t//api/; body is the parsed JSON of a POST, else None.
Local BrowserLocalbrowser operatorempty Optional: the tool's own web browser panel (a palette webBrowser COMP) rendering the same page. While this tab is exposed the console serves the page, so the console switches that browser's Active off, a renderer nobody looks at, and back on when the exposure ends.
Expose to ConsoleAutoregister on / offon Publish this tab into the console while the component exists. Off = the tool runs only its own local interface and contributes nothing.
RegisterRegister button Publish once into the console, even if Expose is off.
UnregisterUnregister button Take this tool's contribution back out of the registry and turn Autoregister off, so it stays out. Register puts it back.
Registration StatusRegstatusread-only textempty Read-only status of this host registration.
Shown in ConsoleDisplayed on / offon Show this tab on the console's tab bar. Off keeps the tab registered but hidden. It stays listed in the console's tab manager, which writes its choice back here.
Tab LabelTablabel textempty What the console's tab bar shows. Empty = the canonical name.
Tab OrderTaborder number50 Position in the tab bar. Built-ins sit at 0 (Settings) and 10 (Install & remove); contributions default to 50 and sort by this, then by label.
Help URLHelpurl textempty The tool's wiki/help page. Informational; leave empty to skip.
Promote ParametersPromotepars on / offon When on, the host mirrors its key Registration parameters onto the parent tool as a bound Registry page. Turn off before shipping a tool that should not expose registry controls.
Pre-Save HealPresaveheal on / offon Before each project save, re-check this registry: update entries whose component moved, drop entries whose component is gone, and republish a host that initialised before the global registry existed. Off skips the sweep for this registry only.

Console

ControlType / defaultWhat it does
Open ConsoleOpen button Serve the console (on the first free port of 36710-36759) and show it, in the toolkit root's webBrowser panel when there is one, else your system browser.

What it adds to a registered tool

Every tool that registers gets these on its own Registry page.

ControlType / defaultWhat it does
Expose to ConsoleAutoregister on / offon Publish this tab into the console while the component exists. Off = the tool runs only its own local interface and contributes nothing.
RegisterRegister button Publish once into the console, even if Expose is off.
UnregisterUnregister button Take this tool's contribution back out of the registry and turn Autoregister off, so it stays out. Register puts it back.
Registration StatusRegstatusread-only textempty Read-only status of this host registration.
Shown in ConsoleDisplayed on / offon Show this tab on the console's tab bar. Off keeps the tab registered but hidden. It stays listed in the console's tab manager, which writes its choice back here.
Tab LabelTablabel textempty What the console's tab bar shows. Empty = the canonical name.
Tab OrderTaborder number50 Position in the tab bar. Built-ins sit at 0 (Settings) and 10 (Install & remove); contributions default to 50 and sort by this, then by label.

Edit this page on GitHub →