{# The figure canvas. ONE workspace, and the canvas is nearly all of it. This markup used to be rendered in two places -- a figure's own page and a split pane beside the live viewer -- and the split pane is gone: composing a figure and looking down a microscope are different activities, and half a window was not enough room for either. What is left is a page whose dominant element is the figure. The permanent chrome is deliberately thin: a narrow tool rail, a tray of panels, and a topbar. Everything that ACTS on a selection is built in JavaScript into #fb_overlay_layer and floats beside what it acts on (figureContextBar.js) -- a fixed properties column would take three hundred pixels away from the thing the user is looking at, permanently, to show controls that are relevant some of the time. #fb_text_panel is not a return of that column, and the distinction is the word "permanently": it is CONTEXTUAL, present only while a text object is selected or being typed into, and gone the rest of the time. Text carries far more formatting than any other object here -- family, size, colour, weight, leading, two alignments -- and a popover deep enough to hold all of it is a popover nobody can work in. It holds formatting only; anything that can be done to any OBJECT stays on the floating bar. That bar renders from figureActions.js, one declaration of what each action is and when it applies, which the right-click menu renders from as well. It used to be four hand-built lists and they had drifted -- the menu disabled "Bring to front" where the bar offered it live on a text box and it did nothing at all. #fb_overlay_layer is the one part of this the canvas renderer never touches. FigureCanvas.render() replaces the whole of #fb_page_surface on every change, so anything that has to survive a render -- the context bar, an open text editor -- lives out here instead. #}
{# Clusters on the desk, not a strip across it. The bar itself paints nothing: what floats is the controls, in the same card as the rail and the tray. The gap between two clusters is what says which controls belong together, which is the job the grey pill inside a solid bar was doing -- without a box inside a box. #}
{# The library is where this goes by default and where it goes if anything is uncertain. It is retargeted at the VIEWER, in JavaScript, when the note left by figureSidebarController says that is where the user came from -- because the commonest trip there is is capture a few fields, look at the figure, go back to the slide for one more, and an arrow that always went to the library made the way back a search. #} {# contenteditable would be fewer elements and the wrong control: it accepts pasted markup, and this value is rendered back into a list of cards. A plain input cannot carry anything but text. #}
{# History, pages and View in one card: three ways of moving around what already exists. Export is not one of those, and does not share it. #}
{# Zoom is not up here. It changes where the eye is, not what the figure is, and sitting in a row with Undo and Export it looked like the same kind of control. It lives in the status bar at the foot of the canvas, beside the readout it moves. #}
{# A native : modal, focus-trapped and Esc-dismissible without a line of script, and it cannot end up behind the canvas the way a positioned div can. #}

Export figure

Panels are re-rendered from the source images at the resolution you ask for — not from what is on screen.

{# Stated up front rather than discovered in the file. Only PNG has an alpha channel; a transparent page rendered to PDF or TIFF is drawn on white, which is a decision the author should make knowingly. #}

A transparent page keeps its transparency in PNG only. PDF and TIFF render it white.

{# Filled by the preflight, before anything is rendered: a warning attached to a file that is already written is a warning nobody acts on. #}
{# This plugin's own buttons, not core's .sidebar-action. The figure page links viewer.css for the borrowed channel widget alone; making the export dialog depend on it too would tie the look of a Figure Builder control to a stylesheet that is here for something else entirely. #}
{# Nothing in here is a column. The canvas runs the full width of the body and the rail and the tray are cards lying on top of it -- which is why .fb-main comes FIRST: it is the only flex child that takes space, and the two cards are positioned against the body and out of the flow. Quick Edit is the exception, and takes its 420px from the canvas, because reframing a panel means watching the figure change as you do it. #}
{# Optional, off by default and remembered per browser. Two strips rather than a wall of positioned ticks: a ruler at 1 mm resolution across an A4 page is 210 elements that have to be rebuilt on every zoom and every scroll. #}
{# The sheet and the two things that belong to it, shrink-wrapped and centred as one block: centred independently, the caption and the button would drift off the page edges they are supposed to line up with. #}
{# Under the panels, and children of the page so they scale with it: a grid drawn in screen pixels drifts off the millimetres it is supposed to represent the moment the zoom changes. #}
{# Guides and the marquee live above the panels and take no pointer events, so a snap line can never be what a drag grabs. #}
{# Under the page and as wide as it. Inside the scroll surface rather than in the status bar, so it scrolls away with the sheet it follows: it is part of the document, not part of the window. #}
{# Never re-rendered by the canvas. See the file comment. #}
{# Level 1: the only controls that are always visible, and there are four of them. Everything else is contextual, in a menu, or on the right-click. There is no Select tool. Selecting is what the pointer does when it is not doing anything else: the drawing tools are one-shot and hand the pointer back the moment they have placed something, and clicking the desk drops one armed by mistake. A button for "no tool" would be a light that is on almost always, and a click nobody needs to make. Scale bar and Legend are not here either. They act on a SELECTION, they have real settings behind them, and both already exist in the floating bar next to the panels they change -- up here they were a second, blunter copy of the same switch, dimmed most of the time. Labelled, at the cost of twenty pixels of width: four icons in a stack with nothing under them is four things to hover before starting, every session, because "a square" is not a word -- and this rail is the first thing a new user has to read. #} {# The text sidebar. CONTEXTUAL, and that is the whole distinction from the fixed properties column this workspace deleted: it is present only while a text object is selected or being typed into, so the canvas keeps the full width for everything else anyone does here. It carries text FORMATTING and nothing else. Actions that apply to any object -- arrange, align, group, duplicate, delete -- belong to the bar that floats over the selection, which is one shared component for every object kind rather than a list rebuilt per type. `.fb-side-panel` is what it shares with the tray below: ONE strip beside the rail, holding one panel at a time, at one size and in one place. Which of them is in it is FigureWorkspace.showSidebar's to say, and neither hides or shows itself -- two panels each answering that for themselves is what drew this one underneath the tray, where it was invisible and still took every click on the overlap. #} {# What this figure is made of. Panels arrive here from the viewer and are dragged onto the page; nothing is ever placed automatically at capture time, because composition is a different sitting from exploration. A card the rail opens, with its own title and its own way out. Open on arrival: a figure page whose panels were behind a closed drawer would look, on the day it is reopened, like a figure that had lost them. #} {# Quick Edit: the microscopy view behind one panel, adjusted without leaving the canvas. The channel controls below the preview are CORE's -- a second ViewerSidebar instance driving the markup below, which carries core's own classes and `fbqe_`-prefixed ids so the two instances can never find each other's elements. Writing a second channel widget here would be two implementations of colour, contrast and ordering that agree until the day one of them is fixed, and a user who has to learn the second one. Deliberately NOT a second scientific viewer: there is no OSD, no tile stack, no overlays and no plugins in here. Anything past reframing and channels is what "Open in Main Viewer" is for. #}
{# What is on the page, and what the pointer can do to it. The topbar acts on the figure; this acts on the view of it. #}
-scroll to zoom · Space-drag to pan
{# The only thing down here anyone clicks, so it is the only thing down here in a card. The count and the hint stay on the desk. #}