Files
clide/lib/widgets/src/clide_spine.dart
T
jpmschweitzerandClaude Opus 4.8 33a8a74240 refactor(i18n): route framework + shared chrome through a 'core' catalog (T-469)
Framework strings outside any extension — widget primitives (collapser, toast,
lightbox, multitab, ex-line, spine, pane chrome), the shared reader chrome, the
markdown 'Open in editor' tooltip, and the drag-resize handle a11y labels — now
resolve under a new 'core' namespace (preloaded at boot). Settles the T-469
namespace question: framework chrome gets one 'core' catalog.

Makes ClideSettings.i18n.string/.interpolated null-safe (ClideKernel.maybeOf):
primitives render kernel-less in isolated tests, returning the placeholder —
matching the D-101 fallback contract for fonts. The markdown tooltip threads
via the ClideMarkdownHooks carrier like mono/ui; drag_resize reads the kernel
i18n directly to avoid a kernel→widgets layering inversion. No en_US change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 23:26:02 +02:00

69 lines
2.3 KiB
Dart

import 'package:clide/widgets/src/clide_settings.dart';
import 'dart:math' as math;
import 'package:clide/widgets/src/clide_tappable.dart';
import 'package:flutter/widgets.dart';
class ClideSpine extends StatelessWidget {
const ClideSpine({super.key, required this.label, required this.onExpand, this.side = SpineSide.left, this.badgeCount = 0});
final String label;
final VoidCallback onExpand;
final SpineSide side;
final int badgeCount;
static const double width = 12;
@override
Widget build(BuildContext context) {
final tokens = ClideSettings.theme.of(context).surface;
final borderSide = BorderSide(color: tokens.dividerColor);
final expandSuffix = ClideSettings.i18n.string(context, 'spine.expandSuffix', namespace: 'core', placeholder: 'click to expand');
return Semantics(
button: true,
label: '$label$expandSuffix',
child: ClideTappable(
onTap: onExpand,
builder: (context, hovered, _) => Container(
width: ClideSpine.width,
decoration: BoxDecoration(
color: hovered ? tokens.sidebarItemHover : tokens.chromeBackground,
border: Border(left: side == SpineSide.right ? borderSide : BorderSide.none, right: side == SpineSide.left ? borderSide : BorderSide.none),
),
child: Stack(
children: [
Center(
child: Transform.rotate(
angle: side == SpineSide.left ? -math.pi / 2 : math.pi / 2,
child: Text(
label,
style: TextStyle(fontSize: 9, color: tokens.globalTextMuted, letterSpacing: 0.5),
maxLines: 1,
overflow: TextOverflow.clip,
),
),
),
if (badgeCount > 0)
Positioned(
top: 4,
left: 0,
right: 0,
child: Center(
child: Container(
width: 6,
height: 6,
decoration: BoxDecoration(color: tokens.statusInfo, shape: BoxShape.circle),
),
),
),
],
),
),
),
);
}
}
enum SpineSide { left, right }