The tabbed central zone

The central zone replaces whole-page navigation with tabs, inside a workbench page that carries the dock, the menu, the title bar and the status bar. A tab holds either a page of the software, loaded as it is inside a frame, or a panel from the registry. This page describes the pane model, persistence, and the K.zone API.

What a tab holds

Tab typeContentIdentity
PageA frame onto an existing route, served by the backend, unmodified.Its URL.
PanelA panel from the registry, built into a div by its usual factory.Its panel identifier.

The pages of the software were not rewritten to fit inside a tab. Each is still served by its own route, with its own CSS and scripts, and the zone only places it. That is what made it possible to move all seventeen templates over at once rather than one a month.

The identity of a page tab is its URL. A page that wants its content back on reopening must carry it in its query string; nothing else is kept.

The frame pool

Frames are not children of the panes. They all live flat under body, fixed-positioned, and a single animation loop lays them on the rectangle of the pane that displays them. Moving a frame in the DOM would reload the page it contains, which rules out any other method as soon as a tab has to change pane.

An inactive tab goes to visibility: hidden, never display: none. Leaflet, Chart.js, three.js and jexcel measure their container height on mount: with display: none they find zero and draw on a single pixel.

Panes and splitting

The layout is a binary tree. A leaf carries a list of tabs and its active tab; a split carries a direction, a ratio and two children. An emptied pane collapses and its neighbour takes its place, which guarantees that a zone with no tabs is always reduced to a single pane.

GestureEffect
Drag a tab onto a pane edge, within 25 % of the widthSplits the pane and drops the tab there.
Drag a tab onto the middle of a paneMoves the tab into that pane.
Drag a tab along the tab stripReorders.
Double-click the empty part of a stripMaximises the pane, or returns to the layout.
Wheel over an overflowing stripScrolls horizontally.

Dragging goes through setPointerCapture rather than HTML5 drag-and-drop, which loses the gesture as soon as the cursor enters a frame. The target is computed against the known pane rectangles, not through elementFromPoint, which cannot see under a frame.

Maximising a pane does not modify the tree and is not saved: reopening the application on a maximised pane would suggest the other tabs had disappeared.

Panels in the centre

A panel from the registry opens in the centre with the same factory contract as in a dock: panelId, root, state, saveState, close. A panel is never in two places: opening it in the centre closes it in the dock.

K.zone.ouvrir(url, opts); // a page tab K.zone.ouvrirPanneau('core.carnet'); // a panel tab K.zone.renvoyerAuDock('core.carnet'); K.zone.fermer(ref); K.zone.activer(ref); K.zone.liste(); K.zone.disposition(); // serialisable K.zone.agrandir(pane);

A restored panel tab whose definition does not exist yet shows a wait and subscribes to registry.changed: extension panels register when their sandbox answers, therefore at an arbitrary moment during startup. The wait has no deadline, which describes exactly the case of a tab whose extension has been uninstalled.

Persistence

PointBehaviour
Storage keyisofind_zone_v1, separate from isofind_shell_v1, which carries the dock.
What is keptA list of URLs and panel identifiers, the pane structure, the pinned tabs.
What is notThe internal state of a page: filter, selection, scroll position.
Active tabDesignated by rank, since tab identifiers are rebuilt every session.
LoadingOnly the active tab of each pane gets its frame; the others load on first click.

Reading back is defensive: a URL that is not absolute on this origin is discarded, an empty side of a split is replaced by the other, and unreadable state is ignored rather than blocking startup.

Pinning rails

Two narrow rails border the window, outside the docks. Extension panels appear there automatically, because they have nowhere else to be announced; everything else is pinned by hand. An empty rail is removed from the flow rather than leaving a grey gutter.

A pin is only a reference, resolved on every render: a page URL, a panel identifier or a command identifier. The label therefore follows the language and any renaming, a plugin installed later makes its button live, and a target that has gone shows hollow rather than being silently removed. The button carries the icon a panel declares when it has one, otherwise a two-letter monogram.

Page modals

A modal opened by a page's own code is drawn by that page, with that page's CSS. The zone does not take it over: it gives the frame the whole rectangle of the central zone for the duration of the modal, and the page draws at home. The dock, the rails, the title bar and the tab strips stay visible and clickable.

Only the active tab of its pane can take the full rectangle; otherwise a modal opened by a timer in a background tab would bring that tab to the front.

Going back to page navigation

The workspace.navigation setting switches between onglets and pages. In page mode the sidebar resumes its navigation role and every link loads a whole document, as before V2.0. The core.vue.onglets and core.vue.pages commands do the same from the palette.

The sidebar still exists in the workbench, hidden by CSS. It is not navigation there but the page catalogue: the plus-button picker, the core.go.* commands and the Pages provider of universal search all read it, and module filtering applies to it.

Commands

CommandEffect
zone.onglet.fermerCloses the active tab.
zone.onglet.epinglerPins or unpins the active tab.
zone.scinder.rangee / zone.scinder.colonneSplits the active pane.
zone.volet.agrandirMaximises the active pane, or returns to the layout.
zone.panneau.rendreSends a centred panel back to the dock.
zone.reinitialiserEmpties the zone and its saved state.
core.rail.epingler / core.rail.choisirPins a target to a rail, or opens the picker.

Related pages