NbShellPanel is a self-contained, collapsible panel that negotiates space with its siblings via flexbox. Each panel renders with its own border, background, and a small gap around it, so that stacked panels look visually detached from one another and from their container (similar to panels in Unity or Unreal Engine).
Panels are layer-aware. They paint --nb-c-surface and --nb-c-border, and they derive their depth from nesting: a panel in a shell's main region takes layer 1, because that region is the page ground, and a panel inside another surface goes one deeper again, clamped at layer 3. Placing them inside a .nb-layer-{0-3} container still pins the whole subtree to that layer and restarts the count from there.
Basic usage
A single panel in a flex container grows to fill whatever space is available.
<template>
<NbShellPanel v-model:size="size" title="Output">
<pre>{{ buildLog }}</pre>
</NbShellPanel>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import type { TShellPanelSize } from '@nubisco/ui'
const size = ref<TShellPanelSize>('default')
</script>Stacking panels
When you stack multiple panels in the same flex container, they share space equally in default mode. Click the maximize button on one panel and watch the other collapse to its header. Click the default button on either panel and both restore to equal sharing.
<template>
<div style="display: flex; flex-direction: column; height: 100%;">
<NbShellPanel v-model:size="consoleSize" title="Console">
<pre>{{ logs }}</pre>
</NbShellPanel>
<NbShellPanel v-model:size="problemsSize" title="Problems">
<DiagnosticList :items="problems" />
</NbShellPanel>
</div>
</template>Toolbar slot
Use the #toolbar slot to add actions to the center of the panel header.
<NbShellPanel v-model:size="size" title="Terminal">
<template #toolbar>
<NbButton size="xxs" variant="ghost" @click="clear">Clear</NbButton>
<NbButton size="xxs" variant="ghost" icon="plus" @click="newTab" />
</template>
<pre>{{ output }}</pre>
</NbShellPanel>Custom controls
Override the default size buttons by using the #controls slot. This lets you provide your own resize logic or entirely different header actions.
<NbShellPanel title="Preview">
<template #controls>
<NbButton size="xxs" variant="ghost" icon="close" @click="hide" />
</template>
<iframe src="/preview" />
</NbShellPanel>Layer integration
Panels use --nb-c-surface and --nb-c-border for their background and border, resolved from the layer they derive. Pinning the container to a layer overrides that derivation and the panels contrast against it.
<!-- Panels adapt to the layer they sit in -->
<div class="nb-layer-0">
<NbShellPanel title="Console"> ... </NbShellPanel>
</div>
<div class="nb-layer-2">
<NbShellPanel title="Properties"> ... </NbShellPanel>
</div>IDE-like layout with NbShell
Panels can be placed in any region of NbShell. Stack them in the main area, the inspector, the bottom slot, or any combination. The panels negotiate space independently within each flex container, and the gaps between them provide the visual separation that makes complex layouts readable.
<template>
<NbShell :inspector-visible="true" inspector-size="sm" :main-padding="false">
<template #sidebar-logo> ... </template>
<template #sidebar-nav> ... </template>
<template #topbar-left> <strong>Editor</strong> </template>
<!-- Main area: viewport + console stacked vertically -->
<div style="display: flex; flex-direction: column; height: 100%;">
<Viewport style="flex: 1;" />
<NbShellPanel v-model:size="consoleSize" title="Console">
<pre>{{ logs }}</pre>
</NbShellPanel>
</div>
<!-- Inspector: two panels sharing space -->
<template #inspector>
<div style="display: flex; flex-direction: column; height: 100%;">
<NbShellPanel v-model:size="propsSize" title="Properties">
<PropertyEditor :node="selected" />
</NbShellPanel>
<NbShellPanel v-model:size="matsSize" title="Materials">
<MaterialEditor :node="selected" />
</NbShellPanel>
</div>
</template>
</NbShell>
</template>Using inside NbShell's bottom slot
NbShellPanel works inside the #bottom slot of NbShell. When a panel reaches full size there, the shell's CSS hides the main content area and lets the panel fill the body.
<template>
<NbShell>
<template #topbar-left> <strong>Editor</strong> </template>
<p>Main content.</p>
<template #bottom>
<NbShellPanel v-model:size="panelSize" title="Terminal">
<pre>{{ output }}</pre>
</NbShellPanel>
</template>
</NbShell>
</template>