Selkies
Developer ReferenceDashboardComponents

components/Sidebar

The dashboard sidebar: every control the reference dashboard offers on top of selkies-core, talking to the core through window.postMessage alone.

Renders a draggable toggle handle, the core action buttons (video, audio, microphone, webcam, gamepad), the soft modifier keys and virtual keyboard button for touch clients, the collapsible video, screen, audio, stats, clipboard, files, apps, sharing, gamepads and shortcuts sections, the upload and clipboard notifications, the apps and files modals, and the second-screen placement arrows.

Messages it consumes from the core: serverSettings (the settings payload that gates which controls render and seeds their values), pipelineStatusUpdate and sidebarButtonStatusUpdate, effectiveCursorState, scalingDpiFollowed (the UI-scaling default the core re-derived), clientRoleUpdate, gamingModeUpdate, toggleDashboard and toggleTouchGamepad (the core-owned Ctrl+Shift+M and Ctrl+Shift+G chords), gamepadControl, clipboardContentUpdate, audioDeviceSelected (its own selection mirrored back, so the dropdowns show what the core was told), gamepadButtonUpdate and gamepadAxisUpdate, fileUpload (upload progress and every notification the core raises), and trackpadModeUpdate.

Messages it posts: settings (debounced), pipelineControl, gamepadControl, setManualResolution, resetResolutionToWindow, setScaleLocally, setAntiAliasing, audioDeviceSelected, clipboardUpdateFromUI, clipboardImageUpdate, requestFullscreen, requestGamingMode, mode, setSynth, sidebarVisibilityChanged, TOUCH_GAMEPAD_SETUP, TOUCH_GAMEPAD_VISIBILITY, touchinput:trackpad and touchinput:touch, plus whatever channel a conditional-settings spec propagates through; the stats section (StreamStats.jsx) posts statsOpen and reads its own. The soft keys dispatch synthetic KeyboardEvents on window, and the files section dispatches the requestFileUpload DOM event.

window state it reads: webrtcInput.gamingMode, __SELKIES_STREAMING_MODE__ and __SELKIES_DUAL_MODE__; it sets __selkiesModeSwitching around a transport switch.

Persistence: every setting lives in localStorage under <storageAppName>_<key>, the keys in PER_DISPLAY_SETTINGS gaining a _display2 suffix on the #display2 hash so a secondary display keeps its own values; an explicit user choice of a derived setting writes a _explicit_choice marker beside its value. The theme is stored unprefixed because it is a per-browser preference.

Functions

roundDownToEven()

function roundDownToEven(num): number;

Defined in: components/Sidebar.jsx:210

Parses a dimension and rounds it down to an even number; non-numbers become 0.

Parameters

ParameterType
numany

Returns

number


debounce()

function debounce(func, delay): Function;

Defined in: components/Sidebar.jsx:222

Trailing debounce: only the last call within delay milliseconds runs.

Parameters

ParameterTypeDescription
funcFunction-
delaynumber-

Returns

Function


GamingModeIcon()

function GamingModeIcon(): Element;

Defined in: components/Sidebar.jsx:244

Crosshair marking gaming mode, the wish dashboard's icon for it: one glyph names the control in both front ends and reads as a target, not a plus sign. Drawn to the ink span of the fullscreen brackets beside it, which cover only the middle of their box, so the header pair reads as one size.

Returns

Element


function SelkiesLogo(props): Element;

Defined in: components/Sidebar.jsx:375

The mark from docs/assets/logo/selkies.svg. The gradient identifier is per-instance: two logos sharing one identifier would leave the second unpainted as soon as the instance that owns the definition unmounts.

Parameters

ParameterTypeDescription
props{ width?: number; height?: number; className?: string; t: Function; }-
props.width?number-
props.height?number-
props.className?string-
props.tFunctionTranslator, for the accessible label.

Returns

Element


AppsModal()

function AppsModal(props): Element;

Defined in: components/Sidebar.jsx:423

Catalog of proot-apps with install, remove, update and launch actions, posted as app commands through app-commands.js.

Parameters

ParameterTypeDescription
props{ isOpen: boolean; onClose: () => void; t: Function; commandsAvailable: boolean; commandsKnown: boolean; installedFromServer: string[]; }-
props.isOpenbooleanRenders nothing while false.
props.onClose() => void-
props.tFunctionTranslator.
props.commandsAvailablebooleanWhether the server accepts remote commands; actions are disabled otherwise.
props.commandsKnownbooleanWhether serverSettings have arrived, so the disabled notice is only shown once known.
props.installedFromServerstring[]App names the runner reports installed; the stored list is only a cache of it.

Returns

Element


handleInstall()

function handleInstall(appName): void;

Defined in: components/Sidebar.jsx:550

The apps command contract both dashboards share: app-commands.js posts the selkies-proot wrapper commands and tracks them for rollback, and the installed list is updated optimistically.

Parameters

ParameterType
appNameany

Returns

void


getPrefixedKey()

function getPrefixedKey(key): string;

Defined in: components/Sidebar.jsx:751

The localStorage key for a setting: the session prefix, plus the _display2 suffix for per-display settings on the secondary display.

Parameters

ParameterTypeDescription
keystring-

Returns

string


readStored()

function readStored(key): string;

Defined in: components/Sidebar.jsx:760

Reads a setting's stored value under its prefixed key.

Parameters

ParameterType
keyany

Returns

string


deriveDpi()

function deriveDpi(manual): number;

Defined in: components/Sidebar.jsx:777

The scaling_dpi default, derived as the core derives it (lib/stream-density.js): from a manual resolution when one is set, since that framebuffer decides how large the desktop draws its UI and the local screen says nothing about it, and otherwise from the local display scaling (devicePixelRatio), so the remote UI matches the local environment. An explicit picker value wins over either. A manual resolution is read off its shorter side -- an ultrawide is wide rather than dense -- and snapping to the nearest option puts a density the options do not name on the closest one and clamps at both ends.

Parameters

ParameterType
manualany

Returns

number

One of the dpiScalingOptions values.


manualResolution()

function manualResolution(serverSettings): object;

Defined in: components/Sidebar.jsx:794

The manual resolution in force, as the cores resolve it: a deployment that forces one and names its size wins, since the core takes the server's over the client's there; otherwise the stored pick, and {w: 0, h: 0} for a session sized to its window.

Parameters

ParameterTypeDescription
serverSettingsanyThe server's settings payload.

Returns

object

w
w: number;
h
h: number;

explicitChoiceKey()

function explicitChoiceKey(spec): string;

Defined in: components/Sidebar.jsx:819

The marker key, suffixed onto the already-prefixed value key so it inherits the per-display suffix and a secondary display keeps its own choice.

Parameters

ParameterType
specany

Returns

string


readExplicitStored()

function readExplicitStored(spec): (key) => string;

Defined in: components/Sidebar.jsx:822

A storage reader for a spec that returns the stored value only when it was an explicit choice.

Parameters

ParameterType
specany

Returns

(key) => string


useConditionalSetting()

function useConditionalSetting(
   spec, 
   serverSettings, 
   ctx, 
   deps, 
   read?
): [any, Function];

Defined in: components/Sidebar.jsx:841

Drives a conditional setting: lazy init, then a re-resolve whenever the server settings or any dependency in deps changes, which covers the server sync and the encoder or manual-resolution re-derivation uniformly. The resolver honors explicit choices, so a re-resolve never clobbers a pinned value. A re-resolve writes state rather than deriving during render because the caller edits the value afterwards; deriving would discard that.

Parameters

ParameterTypeDefault valueDescription
specanyundefinedA spec from conditional-settings.js.
serverSettingsanyundefinedThe last serverSettings payload.
ctxanyundefinedResolution context the spec reads.
depsany[]undefinedValues whose change triggers a re-resolve.
read?(key) => stringreadStoredStorage reader.

Returns

[any, Function]

The value and its setter, as useState returns them.


clampToggleHandleTopPct()

function clampToggleHandleTopPct(pct): number;

Defined in: components/Sidebar.jsx:862

Clamps the toggle handle's vertical position, a percentage of the viewport height, so the handle stays fully inside the viewport. The handle's inline top positions its center (Overlay.css keeps the translateY(-50%)), so the clamp is by half the handle height. Without a finite viewport height (headless, pre-layout) that half would be Infinity, so the clamp is a plain 0 to 100 until a real height is known; a handle at least as tall as the viewport inverts the bounds, so it is centered instead of pinned to an edge.

Parameters

ParameterTypeDescription
pctnumber-

Returns

number


function Sidebar(): Element;

Defined in: components/Sidebar.jsx:876

The sidebar component; see the module docblock for the message and storage contract it implements. Renders nothing when the server hides the sidebar (ui_show_sidebar), and viewer-role clients get no toggle handle.

Returns

Element


launchWindow()

function launchWindow(direction, screen?): boolean;

Defined in: components/Sidebar.jsx:1073

Opens the secondary display in a new window, sized to screen when the Window Management API found one in that direction.

Parameters

ParameterTypeDefault valueDescription
direction"up" | "down" | "left" | "right"undefined-
screen?anynull-

Returns

boolean

Whether the window opened.


handleAddScreenClick()

function handleAddScreenClick(): Promise<void>;

Defined in: components/Sidebar.jsx:1107

Add Screen: places the second display on an adjacent physical screen when the Window Management API reports exactly one, and otherwise offers the placement arrows. Asking beats guessing: the API answers nothing without the window-management permission, and a display silently opened to the right of a monitor that sits above or left of this one is the one thing the arrows exist to avoid. A refused popup falls back to the arrows too, so the button is never seen to do nothing at all.

Returns

Promise<void>


sendKeyEvent()

function sendKeyEvent(
   type, 
   key, 
   code, 
   modifierState
): void;

Defined in: components/Sidebar.jsx:1194

Dispatches a synthetic keyboard event on window for the input core to forward.

Parameters

ParameterType
typeany
keyany
codeany
modifierStateany

Returns

void


handleHoldKeyClick()

function handleHoldKeyClick(key, code): void;

Defined in: components/Sidebar.jsx:1210

Soft modifier key: toggles the key held, and switches the core's synth mode on with the first held modifier and off with the last release.

Parameters

ParameterType
keyany
codeany

Returns

void


handleOnceKeyClick()

function handleOnceKeyClick(key, code): void;

Defined in: components/Sidebar.jsx:1232

Soft momentary key: a press and release carrying the held modifiers.

Parameters

ParameterType
keyany
codeany

Returns

void


writeConditional()

function writeConditional(
   spec, 
   uiValue, 
   setValue, 
   opts?
): void;

Defined in: components/Sidebar.jsx:1498

Uniform write path for conditional settings: optimistic setState, an optional persist (explicit choices pin, derived ones do not), and propagation through the spec.

Parameters

ParameterTypeDescription
specany-
uiValueanyThe value as the UI holds it.
setValueFunctionThe setting's state setter.
opts?{ persist?: boolean; }-
opts.persist?boolean-

Returns

void


handleDpiScalingChange()

function handleDpiScalingChange(event): void;

Defined in: components/Sidebar.jsx:1723

UI scaling pick: persisted, so it pins across reloads and stops the startup derived-default post.

Parameters

ParameterType
eventany

Returns

void


handleTogglePointerUp()

function handleTogglePointerUp(e): void;

Defined in: components/Sidebar.jsx:1816

Ends a handle drag and persists its position. pointerId is null when the pointerup had no matching pointerdown (capture lost), and hasPointerCapture(null) coerces to id 0 and could release a foreign capture, so capture is only released for the tracked pointer.

Parameters

ParameterType
eany

Returns

void


function handleCopyLink(textToCopy, label): Promise<void>;

Defined in: components/Sidebar.jsx:1998

Copies a sharing link and reports the outcome as a notification.

Parameters

ParameterType
textToCopyany
labelany

Returns

Promise<void>


rederiveRateControl()

function rederiveRateControl(ctxOverrides): void;

Defined in: components/Sidebar.jsx:2043

Re-derives rate control after an encoder or software-encoding change. Rate control follows those unless pinned by an explicit client or server choice, and a derived change is not persisted, so it keeps following.

Parameters

ParameterTypeDescription
ctxOverridesanyThe value just chosen, ahead of the re-render that would put it in conditionalCtx.

Returns

void


handleEncoderChange()

function handleEncoderChange(event): void;

Defined in: components/Sidebar.jsx:2060

Encoder pick, one knob for both transports; the server switches the pipeline encoder on it. The choice is persisted immediately so conditionalCtx.activeEncoder, which reads localStorage, does not lag during the post debounce and let a serverSettings sync re-derive rate control off the stale encoder.

Parameters

ParameterType
eventany

Returns

void


handleVideoBitrateChange()

function handleVideoBitrateChange(event): void;

Defined in: components/Sidebar.jsx:2085

Video bitrate slider: its value is an index into videoBitrateOptions.

Parameters

ParameterType
eventany

Returns

void


handleRateControlChange()

function handleRateControlChange(event): void;

Defined in: components/Sidebar.jsx:2136

Rate control pick: an explicit choice, persisted so encoder changes stop overriding it.

Parameters

ParameterType
eventany

Returns

void


handleManualWidthChange()

function handleManualWidthChange(event): void;

Defined in: components/Sidebar.jsx:2189

A half-typed size stays in component state: the stored manual_width and manual_height mean "a manual resolution is applied", which the HiDPI and UI-scaling derivations read, so only Set, a preset, or Reset may write them.

Parameters

ParameterType
eventany

Returns

void


handleHidpiToggle()

function handleHidpiToggle(): void;

Defined in: components/Sidebar.jsx:2206

HiDPI toggle: an explicit choice, pinned; the core persists useCssScaling when it applies the message.

Returns

void


deriveHidpiForResolution()

function deriveHidpiForResolution(manual): void;

Defined in: components/Sidebar.jsx:2216

Manual and preset resolutions pair with CSS scaling: HiDPI off when one is set, on when reset, as a derived (unpinned) write through the uniform path. An explicit toggle or a locked or overridden server value pins HiDPI and stops the resolution buttons from re-deriving it.

Parameters

ParameterTypeDescription
manualbooleanWhether a manual resolution is now applied.

Returns

void


deriveDpiForResolution()

function deriveDpiForResolution(): void;

Defined in: components/Sidebar.jsx:2226

A resolution the operator sets carries its own UI-scaling default, since the framebuffer asked for decides how large the desktop draws its UI. Not stored, so it stays a default: a stored pick, or a locked or operator-explicit server value, outranks it as it does at connect.

Returns

void


resetDpiToDerivedDefault()

function resetDpiToDerivedDefault(): void;

Defined in: components/Sidebar.jsx:2242

Reset-to-window also returns UI scaling to its derived default, which with no resolution of its own left to read is the local display's scaling: the pinned client choice is dropped so the derived default governs again, and the value propagates like a user change. Locked or operator-explicit (overridden) values govern scaling instead, the same gate as the startup derived-default post.

Returns

void


resetHidpiToDerivedDefault()

function resetHidpiToDerivedDefault(): void;

Defined in: components/Sidebar.jsx:2260

Reset-to-window also restores HiDPI to its default. Unlike the resolution-derived writes, which respect a pinned choice, a reset means "back to defaults", so the client's own pin is dropped even under an operator-explicit value: use_css_scaling's overridden does not imply locked, and a kept pin would keep outranking the operator's value in the resolution ladder. The operator value when explicit, else the derived default, is then applied without storing; only a locked value leaves everything alone.

Returns

void


handleUseBrowserCursorsToggle()

function handleUseBrowserCursorsToggle(): void;

Defined in: components/Sidebar.jsx:2286

Browser cursors toggle. The core owns persistence: the new preference is propagated and the core reports the effective value back. The next value derives from the displayed one: while multi-monitor forces the toggle on the base preference may be off, and negating the base would silently persist the forced value over the user's real choice.

Returns

void


handleRawPointerMotionToggle()

function handleRawPointerMotionToggle(): void;

Defined in: components/Sidebar.jsx:2290

Raw pointer motion toggle; the core owns persistence, as for browser cursors.

Returns

void


handleMacCmdAsCtrlToggle()

function handleMacCmdAsCtrlToggle(): void;

Defined in: components/Sidebar.jsx:2294

Command-as-Control toggle; the core owns persistence, as above.

Returns

void


toggleClientSetting()

function toggleClientSetting(
   key, 
   value, 
   setValue
): void;

Defined in: components/Sidebar.jsx:2303

One clipboard or shortcut switch: optimistic, then posted like any setting.

Parameters

ParameterType
keyany
valueany
setValueany

Returns

void


exitFullscreen()

function exitFullscreen(): void;

Defined in: components/Sidebar.jsx:2380

Leaves fullscreen through whichever prefixed API exists. Entering is handed to the core, which owns what each mode locks; exiting is the browser's own call either way.

Returns

void


handleClipboardImageUpload()

function handleClipboardImageUpload(event): void;

Defined in: components/Sidebar.jsx:2402

Hands a picked image to the core's clipboardImageUpdate path (a File is a Blob), which sends it through the binary clipboard exactly like a focus-synced local clipboard image; any other file raises a warning notification. Same contract as the wish dashboard.

Parameters

ParameterType
eventany

Returns

void


handleClipboardBlur()

function handleClipboardBlur(event): void;

Defined in: components/Sidebar.jsx:2429

Pushes the edited clipboard text to the server on blur, never a truncated preview.

Parameters

ParameterType
eventany

Returns

void


handleStreamModeChange()

function handleStreamModeChange(event): Promise<void>;

Defined in: components/Sidebar.jsx:2454

Switches the transport through /api/switch, then posts mode so the core reloads into it. window.__selkiesModeSwitching is set before the request because the server tears down the old peer (WebSocket close code 4000) before it responds, and without the flag the active core would surface a spurious "Server disconnected" alert. The request carries this client's own session token, which a controller's is enough for; a stored master token overrides it, and where neither is accepted a 401 prompts for the master token once, keeps it in sessionStorage and retries, dropping one the server rejects so the next attempt re-prompts. A viewer is refused 403 and is not asked for anything. A failed switch clears the flag again, since no reload follows and a kept flag would hide a real disconnect.

Parameters

ParameterType
eventany

Returns

Promise<void>

References

default

Renames and re-exports Sidebar

On this page

Edit on GitHub