move icon rails to bottom strip row aligned with status bar
Icon rails pulled out of _SidebarSlot and _ContextSlot into the bottom row alongside StatusbarHost. All three strips — left icon rail, app strip, right icon rail — now sit on one continuous horizontal line per D-047. Sidebar and context panels render content only; their bottom rails share the status bar row with matched widths. Collapsed spines get a matching-width spacer. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
+53
-41
@@ -173,7 +173,19 @@ class RootLayout extends StatelessWidget {
|
||||
if (statusVisible)
|
||||
SizedBox(
|
||||
height: statusHeight,
|
||||
child: const StatusbarHost(),
|
||||
child: Row(
|
||||
children: [
|
||||
if (sidebarVisible && !sidebarCollapsed)
|
||||
SizedBox(width: sidebarSize, child: _BottomRail(slot: Slots.sidebar))
|
||||
else if (sidebarVisible && sidebarCollapsed)
|
||||
const SizedBox(width: ClideSpine.width),
|
||||
Expanded(child: const StatusbarHost()),
|
||||
if (contextVisible && !contextCollapsed)
|
||||
SizedBox(width: contextSize, child: _BottomRail(slot: Slots.contextPanel))
|
||||
else if (contextVisible && contextCollapsed)
|
||||
const SizedBox(width: ClideSpine.width),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
@@ -285,38 +297,9 @@ class _SidebarSlot extends StatelessWidget {
|
||||
final tokens = ClideTheme.of(context).surface;
|
||||
return Container(
|
||||
color: tokens.sidebarBackground,
|
||||
child: Column(
|
||||
children: [
|
||||
Expanded(child: active.build(context)),
|
||||
ClideIconRail(
|
||||
items: [
|
||||
for (final t in tabs)
|
||||
ClideIconRailItem(
|
||||
id: t.id,
|
||||
icon: _iconFor(t),
|
||||
tooltip: SlotHost._resolveTitle(context, t),
|
||||
),
|
||||
],
|
||||
activeId: activeId,
|
||||
onSelect: onSelect,
|
||||
),
|
||||
],
|
||||
),
|
||||
child: active.build(context),
|
||||
);
|
||||
}
|
||||
|
||||
static ClideIconPainter _iconFor(TabContribution t) {
|
||||
if (t.icon is ClideIconPainter) return t.icon as ClideIconPainter;
|
||||
return switch (t.id) {
|
||||
'files.tree' => PhosphorIcons.folder,
|
||||
'git.panel' => PhosphorIcons.gitBranch,
|
||||
'pql.panel' => PhosphorIcons.magnifyingGlass,
|
||||
'problems.panel' => PhosphorIcons.warningCircle,
|
||||
'decisions.panel' => PhosphorIcons.lightbulb,
|
||||
'tickets.panel' => PhosphorIcons.ticket,
|
||||
_ => PhosphorIcons.circlesFour,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class _WorkspaceSlot extends StatelessWidget {
|
||||
@@ -429,28 +412,57 @@ class _ContextSlot extends StatelessWidget {
|
||||
final tokens = ClideTheme.of(context).surface;
|
||||
return Container(
|
||||
color: tokens.panelBackground,
|
||||
child: Column(
|
||||
children: [
|
||||
Expanded(child: active.build(context)),
|
||||
ClideIconRail(
|
||||
child: active.build(context),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _BottomRail extends StatelessWidget {
|
||||
const _BottomRail({required this.slot});
|
||||
final SlotId slot;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final kernel = ClideKernel.of(context);
|
||||
final tokens = ClideTheme.of(context).surface;
|
||||
return ListenableBuilder(
|
||||
listenable: kernel.panels,
|
||||
builder: (ctx, _) {
|
||||
final tabs = kernel.panels.tabsFor(slot);
|
||||
if (tabs.isEmpty) return Container(color: tokens.statusBarBackground);
|
||||
final activeId = kernel.panels.activeTabIn(slot) ?? tabs.first.id;
|
||||
return Container(
|
||||
color: tokens.statusBarBackground,
|
||||
child: ClideIconRail(
|
||||
items: [
|
||||
for (final t in tabs)
|
||||
ClideIconRailItem(
|
||||
id: t.id,
|
||||
icon: _iconFor(t),
|
||||
tooltip: SlotHost._resolveTitle(context, t),
|
||||
icon: _iconFor(slot, t),
|
||||
tooltip: SlotHost._resolveTitle(ctx, t),
|
||||
),
|
||||
],
|
||||
activeId: activeId,
|
||||
onSelect: onSelect,
|
||||
onSelect: (id) => kernel.panels.activateTab(slot, id),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
static ClideIconPainter _iconFor(TabContribution t) {
|
||||
static ClideIconPainter _iconFor(SlotId slot, TabContribution t) {
|
||||
if (t.icon is ClideIconPainter) return t.icon as ClideIconPainter;
|
||||
if (slot == Slots.sidebar) {
|
||||
return switch (t.id) {
|
||||
'files.tree' => PhosphorIcons.folder,
|
||||
'git.panel' => PhosphorIcons.gitBranch,
|
||||
'pql.panel' => PhosphorIcons.magnifyingGlass,
|
||||
'problems.panel' => PhosphorIcons.warningCircle,
|
||||
'decisions.panel' => PhosphorIcons.lightbulb,
|
||||
'tickets.panel' => PhosphorIcons.ticket,
|
||||
_ => PhosphorIcons.circlesFour,
|
||||
};
|
||||
}
|
||||
return switch (t.id) {
|
||||
'markdown.viewer' => PhosphorIcons.eye,
|
||||
'graph.view' => PhosphorIcons.graph,
|
||||
|
||||
Reference in New Issue
Block a user