chore: adopt Dart 3.9 toolchain — honest floor + tall-style reformat (T-353)
Raise the declared minimums in pubspec.yaml to what our deps already require: Flutter >=3.35.0 / Dart >=3.9.0 (was 3.19.0 / 3.5.0). alchemist 0.12 needs Flutter 3.32; Dart 3.9 first ships in Flutter 3.35, so 3.35 is the binding floor. Pin the exact build toolchain in .fvmrc (Flutter 3.44.1). Moving to the Dart 3.9 language level switches `dart format` to the new "tall" style and enables two new lints. This commit is the resulting mechanical churn, isolated from any behaviour change: - whole-tree `dart format` reformat (tall style) - `dart fix` for unnecessary_underscores + use_null_aware_elements No runtime behaviour change; `make test` green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -39,8 +39,12 @@ class ClideAccordion extends StatelessWidget {
|
||||
ClideIcon(expanded ? PhosphorIcons.byName('caret-down') : PhosphorIcons.byName('caret-right'), size: 10, color: tokens.globalTextMuted),
|
||||
const SizedBox(width: 6),
|
||||
if (leading != null) ...[leading!, const SizedBox(width: 6)],
|
||||
ClideText('$label · $count',
|
||||
fontSize: clideFontSmall, color: hovered ? tokens.globalForeground : tokens.sidebarSectionHeader, fontFamily: clideMonoFamily),
|
||||
ClideText(
|
||||
'$label · $count',
|
||||
fontSize: clideFontSmall,
|
||||
color: hovered ? tokens.globalForeground : tokens.sidebarSectionHeader,
|
||||
fontFamily: clideMonoFamily,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
@@ -186,52 +186,52 @@ class _ClideAnchoredOverlayState extends State<ClideAnchoredOverlay> {
|
||||
a == ClideAnchorAlign.start
|
||||
? Alignment.bottomLeft
|
||||
: a == ClideAnchorAlign.end
|
||||
? Alignment.bottomRight
|
||||
: Alignment.bottomCenter,
|
||||
? Alignment.bottomRight
|
||||
: Alignment.bottomCenter,
|
||||
a == ClideAnchorAlign.start
|
||||
? Alignment.topLeft
|
||||
: a == ClideAnchorAlign.end
|
||||
? Alignment.topRight
|
||||
: Alignment.topCenter,
|
||||
? Alignment.topRight
|
||||
: Alignment.topCenter,
|
||||
);
|
||||
case ClideAnchorSide.above:
|
||||
return (
|
||||
a == ClideAnchorAlign.start
|
||||
? Alignment.topLeft
|
||||
: a == ClideAnchorAlign.end
|
||||
? Alignment.topRight
|
||||
: Alignment.topCenter,
|
||||
? Alignment.topRight
|
||||
: Alignment.topCenter,
|
||||
a == ClideAnchorAlign.start
|
||||
? Alignment.bottomLeft
|
||||
: a == ClideAnchorAlign.end
|
||||
? Alignment.bottomRight
|
||||
: Alignment.bottomCenter,
|
||||
? Alignment.bottomRight
|
||||
: Alignment.bottomCenter,
|
||||
);
|
||||
case ClideAnchorSide.right:
|
||||
return (
|
||||
a == ClideAnchorAlign.start
|
||||
? Alignment.topRight
|
||||
: a == ClideAnchorAlign.end
|
||||
? Alignment.bottomRight
|
||||
: Alignment.centerRight,
|
||||
? Alignment.bottomRight
|
||||
: Alignment.centerRight,
|
||||
a == ClideAnchorAlign.start
|
||||
? Alignment.topLeft
|
||||
: a == ClideAnchorAlign.end
|
||||
? Alignment.bottomLeft
|
||||
: Alignment.centerLeft,
|
||||
? Alignment.bottomLeft
|
||||
: Alignment.centerLeft,
|
||||
);
|
||||
case ClideAnchorSide.left:
|
||||
return (
|
||||
a == ClideAnchorAlign.start
|
||||
? Alignment.topLeft
|
||||
: a == ClideAnchorAlign.end
|
||||
? Alignment.bottomLeft
|
||||
: Alignment.centerLeft,
|
||||
? Alignment.bottomLeft
|
||||
: Alignment.centerLeft,
|
||||
a == ClideAnchorAlign.start
|
||||
? Alignment.topRight
|
||||
: a == ClideAnchorAlign.end
|
||||
? Alignment.bottomRight
|
||||
: Alignment.centerRight,
|
||||
? Alignment.bottomRight
|
||||
: Alignment.centerRight,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,8 +49,8 @@ class ClideButton extends StatelessWidget {
|
||||
bg = pressed
|
||||
? tokens.buttonActiveBackground
|
||||
: hovered
|
||||
? tokens.buttonHoverBackground
|
||||
: tokens.buttonBackground;
|
||||
? tokens.buttonHoverBackground
|
||||
: tokens.buttonBackground;
|
||||
fg = tokens.buttonForeground;
|
||||
case ClideButtonVariant.primary:
|
||||
bg = pressed ? tokens.panelActiveBorder : tokens.buttonActiveBackground;
|
||||
@@ -67,11 +67,7 @@ class ClideButton extends StatelessWidget {
|
||||
border: Border.all(color: tokens.buttonBorder, width: 1),
|
||||
borderRadius: BorderRadius.circular(3),
|
||||
),
|
||||
child: ClideText(
|
||||
label,
|
||||
color: fg,
|
||||
fontWeight: variant == ClideButtonVariant.primary ? FontWeight.w600 : FontWeight.w500,
|
||||
),
|
||||
child: ClideText(label, color: fg, fontWeight: variant == ClideButtonVariant.primary ? FontWeight.w600 : FontWeight.w500),
|
||||
);
|
||||
},
|
||||
),
|
||||
|
||||
@@ -50,12 +50,7 @@ class _ClideCodeBlockState extends State<ClideCodeBlock> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final tokens = ClideTheme.of(context).surface;
|
||||
final style = TextStyle(
|
||||
fontFamily: clideMonoFamily,
|
||||
fontFamilyFallback: clideMonoFamilyFallback,
|
||||
fontSize: clideFontMono,
|
||||
color: tokens.globalForeground,
|
||||
);
|
||||
final style = TextStyle(fontFamily: clideMonoFamily, fontFamilyFallback: clideMonoFamilyFallback, fontSize: clideFontMono, color: tokens.globalForeground);
|
||||
|
||||
final spans = _spans;
|
||||
TextSpan textSpan;
|
||||
@@ -74,10 +69,7 @@ class _ClideCodeBlockState extends State<ClideCodeBlock> {
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
border: Border.all(color: tokens.panelBorder),
|
||||
),
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: Text.rich(textSpan),
|
||||
),
|
||||
child: SingleChildScrollView(scrollDirection: Axis.horizontal, child: Text.rich(textSpan)),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -114,7 +106,12 @@ class _ClideCodeBlockState extends State<ClideCodeBlock> {
|
||||
}
|
||||
if (eChar > clippedStart) {
|
||||
final color = TreeSitterService.colorForRole(span.role, tokens);
|
||||
children.add(TextSpan(text: source.substring(clippedStart, eChar), style: base.copyWith(color: color)));
|
||||
children.add(
|
||||
TextSpan(
|
||||
text: source.substring(clippedStart, eChar),
|
||||
style: base.copyWith(color: color),
|
||||
),
|
||||
);
|
||||
}
|
||||
if (eChar > lastChar) lastChar = eChar;
|
||||
}
|
||||
|
||||
@@ -140,98 +140,87 @@ class _ClideCollapserCardState extends State<ClideCollapserCard> {
|
||||
),
|
||||
],
|
||||
// Status — the icon hard against the right edge.
|
||||
if (widget.status != null) ...[
|
||||
const SizedBox(width: 8),
|
||||
ClideStatusIndicator(status: widget.status!, size: clideIconHero),
|
||||
],
|
||||
if (widget.status != null) ...[const SizedBox(width: 8), ClideStatusIndicator(status: widget.status!, size: clideIconHero)],
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
/// Collapsed: the ticker row IS the toggle, focusable for keyboard/AT.
|
||||
Widget _tickerRow(SurfaceTokens tokens) => ClideTappable(
|
||||
focusNode: _controlFocus,
|
||||
onTap: _toggle,
|
||||
tooltip: 'Expand',
|
||||
builder: (context, hovered, focused) => Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: kClideCardHeaderPadH, vertical: kClideCardHeaderPadV),
|
||||
decoration: BoxDecoration(
|
||||
color: (hovered || focused) ? tokens.listItemHoverBackground : tokens.listItemBackground,
|
||||
border: Border.all(color: widget.color ?? tokens.panelBorder),
|
||||
borderRadius: BorderRadius.circular(kClideCardRadius),
|
||||
),
|
||||
child: _headerContent(tokens, expanded: false),
|
||||
),
|
||||
);
|
||||
focusNode: _controlFocus,
|
||||
onTap: _toggle,
|
||||
tooltip: 'Expand',
|
||||
builder: (context, hovered, focused) => Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: kClideCardHeaderPadH, vertical: kClideCardHeaderPadV),
|
||||
decoration: BoxDecoration(
|
||||
color: (hovered || focused) ? tokens.listItemHoverBackground : tokens.listItemBackground,
|
||||
border: Border.all(color: widget.color ?? tokens.panelBorder),
|
||||
borderRadius: BorderRadius.circular(kClideCardRadius),
|
||||
),
|
||||
child: _headerContent(tokens, expanded: false),
|
||||
),
|
||||
);
|
||||
|
||||
/// Expanded: a framed inner canvas wrapping the item cards. The frame
|
||||
/// BACKGROUND is a gesture target behind the items that only fires for hits
|
||||
/// the items don't consume.
|
||||
Widget _expandedFrame(SurfaceTokens tokens) => DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: widget.color ?? tokens.panelBorder),
|
||||
borderRadius: BorderRadius.circular(kClideCardRadius),
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: widget.color ?? tokens.panelBorder),
|
||||
borderRadius: BorderRadius.circular(kClideCardRadius),
|
||||
),
|
||||
child: Stack(
|
||||
children: [
|
||||
// Background toggle: behind the items, not a whole-card overlay, so
|
||||
// item taps are never intercepted. Excluded from focus traversal —
|
||||
// the header caret is the single keyboard stop.
|
||||
Positioned.fill(
|
||||
child: ExcludeFocus(
|
||||
child: ClideTappable(onTap: _toggle, tooltip: 'Collapse', builder: (_, _, _) => const SizedBox.expand()),
|
||||
),
|
||||
),
|
||||
child: Stack(
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
// Background toggle: behind the items, not a whole-card overlay, so
|
||||
// item taps are never intercepted. Excluded from focus traversal —
|
||||
// the header caret is the single keyboard stop.
|
||||
Positioned.fill(
|
||||
child: ExcludeFocus(
|
||||
child: ClideTappable(
|
||||
onTap: _toggle,
|
||||
tooltip: 'Collapse',
|
||||
builder: (_, __, ___) => const SizedBox.expand(),
|
||||
),
|
||||
_headerRow(tokens),
|
||||
// Even padding around the inner item canvas (T-305): the sides +
|
||||
// top match, and each inner item carries a matching bottom margin
|
||||
// (so the last item's margin is the bottom inset and items in a
|
||||
// multi-item run are evenly separated) — hence bottom 0 here.
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(kClideCardHeaderPadH, kClideCardHeaderPadH, kClideCardHeaderPadH, 0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
for (final child in widget.children)
|
||||
// Each item opaquely consumes its full bounds so a body
|
||||
// tap interacts with that card (or does nothing), never
|
||||
// the collapser background. Deeper controls still win;
|
||||
// only taps are absorbed, so selection drags pass through.
|
||||
GestureDetector(behavior: HitTestBehavior.opaque, onTap: () {}, child: child),
|
||||
],
|
||||
),
|
||||
),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
_headerRow(tokens),
|
||||
// Even padding around the inner item canvas (T-305): the sides +
|
||||
// top match, and each inner item carries a matching bottom margin
|
||||
// (so the last item's margin is the bottom inset and items in a
|
||||
// multi-item run are evenly separated) — hence bottom 0 here.
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(kClideCardHeaderPadH, kClideCardHeaderPadH, kClideCardHeaderPadH, 0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
for (final child in widget.children)
|
||||
// Each item opaquely consumes its full bounds so a body
|
||||
// tap interacts with that card (or does nothing), never
|
||||
// the collapser background. Deeper controls still win;
|
||||
// only taps are absorbed, so selection drags pass through.
|
||||
GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onTap: () {},
|
||||
child: child,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
/// The explicit, focusable collapse control in the expanded header. A
|
||||
/// background tap alone is not keyboard/AT reachable, so this keeps the
|
||||
/// control on the Tab path and Enter/Space-activatable (D-78).
|
||||
Widget _headerRow(SurfaceTokens tokens) => ClideTappable(
|
||||
focusNode: _controlFocus,
|
||||
onTap: _toggle,
|
||||
tooltip: 'Collapse',
|
||||
builder: (context, hovered, focused) => Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: kClideCardHeaderPadH, vertical: kClideCardHeaderPadV),
|
||||
decoration: BoxDecoration(
|
||||
color: (hovered || focused) ? tokens.listItemHoverBackground : tokens.listItemBackground,
|
||||
border: Border(bottom: BorderSide(color: widget.color ?? tokens.panelBorder)),
|
||||
),
|
||||
child: _headerContent(tokens, expanded: true),
|
||||
),
|
||||
);
|
||||
focusNode: _controlFocus,
|
||||
onTap: _toggle,
|
||||
tooltip: 'Collapse',
|
||||
builder: (context, hovered, focused) => Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: kClideCardHeaderPadH, vertical: kClideCardHeaderPadV),
|
||||
decoration: BoxDecoration(
|
||||
color: (hovered || focused) ? tokens.listItemHoverBackground : tokens.listItemBackground,
|
||||
border: Border(bottom: BorderSide(color: widget.color ?? tokens.panelBorder)),
|
||||
),
|
||||
child: _headerContent(tokens, expanded: true),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -117,11 +117,7 @@ class _WinButton extends StatelessWidget {
|
||||
height: hatHeight,
|
||||
color: hovered ? hoverBg : null,
|
||||
alignment: Alignment.center,
|
||||
child: ClideIcon(
|
||||
icon,
|
||||
size: 14,
|
||||
color: hovered && isClose ? tokens.windowControlCloseHoverForeground : tokens.globalTextMuted,
|
||||
),
|
||||
child: ClideIcon(icon, size: 14, color: hovered && isClose ? tokens.windowControlCloseHoverForeground : tokens.globalTextMuted),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,11 +2,7 @@ import 'package:clide/kernel/src/theme/controller.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
class ClideDivider extends StatelessWidget {
|
||||
const ClideDivider({
|
||||
super.key,
|
||||
this.axis = Axis.horizontal,
|
||||
this.thickness = 1,
|
||||
});
|
||||
const ClideDivider({super.key, this.axis = Axis.horizontal, this.thickness = 1});
|
||||
|
||||
final Axis axis;
|
||||
final double thickness;
|
||||
@@ -14,10 +10,6 @@ class ClideDivider extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final color = ClideTheme.of(context).surface.dividerColor;
|
||||
return Container(
|
||||
width: axis == Axis.vertical ? thickness : null,
|
||||
height: axis == Axis.horizontal ? thickness : null,
|
||||
color: color,
|
||||
);
|
||||
return Container(width: axis == Axis.vertical ? thickness : null, height: axis == Axis.horizontal ? thickness : null, color: color);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,9 +13,7 @@ import 'package:flutter/widgets.dart';
|
||||
/// into `==`/`hashCode` makes an overwrite a fresh key → a cache miss → a
|
||||
/// re-read of the current bytes; an unchanged file still hits the cache.
|
||||
class ClideFileImage extends FileImage {
|
||||
ClideFileImage(String path, {double scale = 1.0})
|
||||
: _stamp = _stampOf(path),
|
||||
super(File(path), scale: scale);
|
||||
ClideFileImage(String path, {double scale = 1.0}) : _stamp = _stampOf(path), super(File(path), scale: scale);
|
||||
|
||||
/// mtime ⊕ size — changes on any in-place overwrite (a write bumps mtime; a
|
||||
/// different length bumps size even within one clock tick). 0 if the file
|
||||
|
||||
@@ -29,12 +29,7 @@ class EmptyIconPainter extends ClideIconPainter {
|
||||
}
|
||||
|
||||
class ClideIcon extends StatelessWidget {
|
||||
const ClideIcon(
|
||||
this.painter, {
|
||||
super.key,
|
||||
this.size = 14,
|
||||
this.color,
|
||||
});
|
||||
const ClideIcon(this.painter, {super.key, this.size = 14, this.color});
|
||||
|
||||
final ClideIconPainter painter;
|
||||
final double size;
|
||||
|
||||
@@ -4,11 +4,7 @@ import 'package:clide/widgets/src/clide_tappable.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
class ClideIconRailItem {
|
||||
const ClideIconRailItem({
|
||||
required this.id,
|
||||
required this.icon,
|
||||
required this.tooltip,
|
||||
});
|
||||
const ClideIconRailItem({required this.id, required this.icon, required this.tooltip});
|
||||
|
||||
final String id;
|
||||
final ClideIconPainter icon;
|
||||
@@ -16,12 +12,7 @@ class ClideIconRailItem {
|
||||
}
|
||||
|
||||
class ClideIconRail extends StatelessWidget {
|
||||
const ClideIconRail({
|
||||
super.key,
|
||||
required this.items,
|
||||
required this.activeId,
|
||||
required this.onSelect,
|
||||
});
|
||||
const ClideIconRail({super.key, required this.items, required this.activeId, required this.onSelect});
|
||||
|
||||
final List<ClideIconRailItem> items;
|
||||
final String? activeId;
|
||||
@@ -42,14 +33,7 @@ class ClideIconRail extends StatelessWidget {
|
||||
constraints: BoxConstraints(minWidth: minWidth),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
for (final item in items)
|
||||
_RailButton(
|
||||
item: item,
|
||||
active: item.id == activeId,
|
||||
onTap: () => onSelect(item.id),
|
||||
),
|
||||
],
|
||||
children: [for (final item in items) _RailButton(item: item, active: item.id == activeId, onTap: () => onSelect(item.id))],
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -59,11 +43,7 @@ class ClideIconRail extends StatelessWidget {
|
||||
}
|
||||
|
||||
class _RailButton extends StatelessWidget {
|
||||
const _RailButton({
|
||||
required this.item,
|
||||
required this.active,
|
||||
required this.onTap,
|
||||
});
|
||||
const _RailButton({required this.item, required this.active, required this.onTap});
|
||||
|
||||
final ClideIconRailItem item;
|
||||
final bool active;
|
||||
@@ -83,17 +63,12 @@ class _RailButton extends StatelessWidget {
|
||||
final color = active
|
||||
? tokens.globalForeground
|
||||
: hovered
|
||||
? tokens.sidebarForeground
|
||||
: tokens.sidebarSectionHeader;
|
||||
? tokens.sidebarForeground
|
||||
: tokens.sidebarSectionHeader;
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
|
||||
decoration: BoxDecoration(
|
||||
border: Border(
|
||||
bottom: BorderSide(
|
||||
color: active ? tokens.tabActiveBorder : const Color(0x00000000),
|
||||
width: 2,
|
||||
),
|
||||
),
|
||||
border: Border(bottom: BorderSide(color: active ? tokens.tabActiveBorder : const Color(0x00000000), width: 2)),
|
||||
),
|
||||
child: ClideIcon(item.icon, size: 16, color: color),
|
||||
);
|
||||
|
||||
@@ -22,13 +22,7 @@ import 'package:flutter/services.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
class ClideLightbox extends StatefulWidget {
|
||||
const ClideLightbox({
|
||||
super.key,
|
||||
required this.child,
|
||||
required this.onDismiss,
|
||||
this.minScale = 0.5,
|
||||
this.maxScale = 8.0,
|
||||
});
|
||||
const ClideLightbox({super.key, required this.child, required this.onDismiss, this.minScale = 0.5, this.maxScale = 8.0});
|
||||
|
||||
/// The content to zoom — constrained to the viewport on open (pass an image
|
||||
/// with `fit: BoxFit.contain` so it fits, then scales on zoom).
|
||||
@@ -159,17 +153,10 @@ class _ClideLightboxState extends State<ClideLightbox> {
|
||||
right: 0,
|
||||
child: Center(
|
||||
child: DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
color: tokens.panelHeader,
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
),
|
||||
decoration: BoxDecoration(color: tokens.panelHeader, borderRadius: BorderRadius.circular(4)),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3),
|
||||
child: ClideText(
|
||||
'scroll to zoom · double-click to reset · Esc to close',
|
||||
fontSize: clideFontMeta,
|
||||
color: tokens.globalTextMuted,
|
||||
),
|
||||
child: ClideText('scroll to zoom · double-click to reset · Esc to close', fontSize: clideFontMeta, color: tokens.globalTextMuted),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -118,11 +118,7 @@ class ClideMarkdown extends StatelessWidget {
|
||||
onOpenFile: onOpenFile,
|
||||
);
|
||||
final widgets = _buildNodes(nodes, tokens, hooks);
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: widgets,
|
||||
);
|
||||
return Column(crossAxisAlignment: CrossAxisAlignment.stretch, mainAxisSize: MainAxisSize.min, children: widgets);
|
||||
}
|
||||
|
||||
static const _inlineTags = {'strong', 'em', 'code', 'a', 'del', 'br', 'img', 'span'};
|
||||
@@ -147,19 +143,21 @@ class ClideMarkdown extends StatelessWidget {
|
||||
spans.add(_inlineElementSpan(n, tokens, hooks));
|
||||
}
|
||||
}
|
||||
out.add(Text.rich(
|
||||
TextSpan(
|
||||
style: TextStyle(
|
||||
fontFamily: clideUiFamily,
|
||||
fontFamilyFallback: clideUiFamilyFallback,
|
||||
fontWeight: clideUiDefaultWeight,
|
||||
color: tokens.globalForeground,
|
||||
fontSize: _fontSize,
|
||||
height: _lineHeight,
|
||||
out.add(
|
||||
Text.rich(
|
||||
TextSpan(
|
||||
style: TextStyle(
|
||||
fontFamily: clideUiFamily,
|
||||
fontFamilyFallback: clideUiFamilyFallback,
|
||||
fontWeight: clideUiDefaultWeight,
|
||||
color: tokens.globalForeground,
|
||||
fontSize: _fontSize,
|
||||
height: _lineHeight,
|
||||
),
|
||||
children: spans,
|
||||
),
|
||||
children: spans,
|
||||
),
|
||||
));
|
||||
);
|
||||
inlineRun.clear();
|
||||
}
|
||||
|
||||
@@ -202,10 +200,7 @@ class ClideMarkdown extends StatelessWidget {
|
||||
child: _inlineText(el, tokens, hooks, fontSize: clideFontBody, fontWeight: FontWeight.w600),
|
||||
);
|
||||
case 'p':
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 14),
|
||||
child: _inlineRichText(el, tokens, hooks),
|
||||
);
|
||||
return Padding(padding: const EdgeInsets.only(bottom: 14), child: _inlineRichText(el, tokens, hooks));
|
||||
case 'ul':
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(left: 2, bottom: 14),
|
||||
@@ -214,7 +209,7 @@ class ClideMarkdown extends StatelessWidget {
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
for (final c in el.children ?? const [])
|
||||
if (c is md.Element) _buildListItem(c, tokens, hooks, ordered: false)
|
||||
if (c is md.Element) _buildListItem(c, tokens, hooks, ordered: false),
|
||||
],
|
||||
),
|
||||
);
|
||||
@@ -234,7 +229,9 @@ class ClideMarkdown extends StatelessWidget {
|
||||
return Container(
|
||||
margin: const EdgeInsets.only(bottom: 14),
|
||||
padding: const EdgeInsets.only(left: 6),
|
||||
decoration: BoxDecoration(border: Border(left: BorderSide(color: tokens.globalTextMuted, width: 3))),
|
||||
decoration: BoxDecoration(
|
||||
border: Border(left: BorderSide(color: tokens.globalTextMuted, width: 3)),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
@@ -256,10 +253,7 @@ class ClideMarkdown extends StatelessWidget {
|
||||
case 'hr':
|
||||
return Padding(padding: const EdgeInsets.symmetric(vertical: 8), child: ClideDivider());
|
||||
case 'table':
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 8),
|
||||
child: _buildTable(el, tokens, hooks),
|
||||
);
|
||||
return Padding(padding: const EdgeInsets.only(bottom: 8), child: _buildTable(el, tokens, hooks));
|
||||
default:
|
||||
return _inlineRichText(el, tokens, hooks);
|
||||
}
|
||||
@@ -295,16 +289,24 @@ class ClideMarkdown extends StatelessWidget {
|
||||
final isHeader = row.tag == 'tr' && (child.tag == 'thead');
|
||||
for (final cell in row.children ?? const []) {
|
||||
if (cell is! md.Element) continue;
|
||||
cells.add(Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 3),
|
||||
child: _inlineText(cell, tokens, hooks, fontWeight: isHeader ? FontWeight.w600 : null),
|
||||
));
|
||||
cells.add(
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 3),
|
||||
child: _inlineText(cell, tokens, hooks, fontWeight: isHeader ? FontWeight.w600 : null),
|
||||
),
|
||||
);
|
||||
}
|
||||
if (cells.isNotEmpty) {
|
||||
rows.add(TableRow(
|
||||
decoration: isHeader ? BoxDecoration(border: Border(bottom: BorderSide(color: tokens.dividerColor))) : null,
|
||||
children: cells,
|
||||
));
|
||||
rows.add(
|
||||
TableRow(
|
||||
decoration: isHeader
|
||||
? BoxDecoration(
|
||||
border: Border(bottom: BorderSide(color: tokens.dividerColor)),
|
||||
)
|
||||
: null,
|
||||
children: cells,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -353,7 +355,7 @@ class ClideMarkdown extends StatelessWidget {
|
||||
style: const TextStyle(fontWeight: FontWeight.w700),
|
||||
children: [
|
||||
for (final c in el.children ?? const [])
|
||||
if (c is md.Text) ..._linkifyText(_unescapeHtml(c.text), tokens, hooks) else if (c is md.Element) _inlineElementSpan(c, tokens, hooks)
|
||||
if (c is md.Text) ..._linkifyText(_unescapeHtml(c.text), tokens, hooks) else if (c is md.Element) _inlineElementSpan(c, tokens, hooks),
|
||||
],
|
||||
);
|
||||
case 'em':
|
||||
@@ -361,7 +363,7 @@ class ClideMarkdown extends StatelessWidget {
|
||||
style: const TextStyle(fontStyle: FontStyle.italic),
|
||||
children: [
|
||||
for (final c in el.children ?? const [])
|
||||
if (c is md.Text) ..._linkifyText(_unescapeHtml(c.text), tokens, hooks) else if (c is md.Element) _inlineElementSpan(c, tokens, hooks)
|
||||
if (c is md.Text) ..._linkifyText(_unescapeHtml(c.text), tokens, hooks) else if (c is md.Element) _inlineElementSpan(c, tokens, hooks),
|
||||
],
|
||||
);
|
||||
case 'code':
|
||||
@@ -420,10 +422,12 @@ class ClideMarkdown extends StatelessWidget {
|
||||
var last = 0;
|
||||
for (final m in _imageTokenPattern.allMatches(text)) {
|
||||
if (m.start > last) spans.addAll(_linkifyProse(text.substring(last, m.start), tokens, hooks));
|
||||
spans.add(WidgetSpan(
|
||||
alignment: PlaceholderAlignment.middle,
|
||||
child: Padding(padding: const EdgeInsets.symmetric(horizontal: 2), child: hooks.onImageToken!(m.group(1)!)),
|
||||
));
|
||||
spans.add(
|
||||
WidgetSpan(
|
||||
alignment: PlaceholderAlignment.middle,
|
||||
child: Padding(padding: const EdgeInsets.symmetric(horizontal: 2), child: hooks.onImageToken!(m.group(1)!)),
|
||||
),
|
||||
);
|
||||
last = m.end;
|
||||
}
|
||||
if (last < text.length) spans.addAll(_linkifyProse(text.substring(last), tokens, hooks));
|
||||
@@ -503,7 +507,7 @@ class ClideMarkdown extends StatelessWidget {
|
||||
baseline: TextBaseline.alphabetic,
|
||||
child: ClideTappable(
|
||||
onTap: () => onRecordTap(id),
|
||||
builder: (_, hovered, __) => Text(
|
||||
builder: (_, hovered, _) => Text(
|
||||
id,
|
||||
style: TextStyle(
|
||||
color: tokens.globalFocus,
|
||||
@@ -534,7 +538,7 @@ class ClideMarkdown extends StatelessWidget {
|
||||
child: ClideTappable(
|
||||
onTap: () => onOpenFile(absPath, line),
|
||||
tooltip: 'Open in editor',
|
||||
builder: (_, hovered, __) => Text(
|
||||
builder: (_, hovered, _) => Text(
|
||||
display,
|
||||
style: TextStyle(
|
||||
color: tokens.globalFocus,
|
||||
@@ -560,7 +564,7 @@ class ClideMarkdown extends StatelessWidget {
|
||||
child: ClideTappable(
|
||||
onTap: () => onLinkTap(href),
|
||||
tooltip: href,
|
||||
builder: (_, hovered, __) => Text(
|
||||
builder: (_, hovered, _) => Text(
|
||||
text,
|
||||
style: TextStyle(
|
||||
color: tokens.globalFocus,
|
||||
|
||||
@@ -10,12 +10,7 @@ import 'package:flutter/scheduler.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
class ClideMarquee extends StatefulWidget {
|
||||
const ClideMarquee({
|
||||
super.key,
|
||||
required this.child,
|
||||
this.pxPerSecond = 28,
|
||||
this.gap = 48,
|
||||
});
|
||||
const ClideMarquee({super.key, required this.child, this.pxPerSecond = 28, this.gap = 48});
|
||||
|
||||
final Widget child;
|
||||
|
||||
|
||||
@@ -254,9 +254,7 @@ class _ClideMenuState extends State<ClideMenu> {
|
||||
final col = Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
for (var i = 0; i < widget.entries.length; i++) _row(i, widget.entries[i], t),
|
||||
],
|
||||
children: [for (var i = 0; i < widget.entries.length; i++) _row(i, widget.entries[i], t)],
|
||||
);
|
||||
return Focus(
|
||||
focusNode: _focus,
|
||||
@@ -264,11 +262,7 @@ class _ClideMenuState extends State<ClideMenu> {
|
||||
onKeyEvent: _onKey,
|
||||
child: IntrinsicWidth(
|
||||
child: Container(
|
||||
constraints: BoxConstraints(
|
||||
minWidth: widget.minWidth,
|
||||
maxWidth: widget.maxWidth,
|
||||
maxHeight: widget.maxHeight ?? double.infinity,
|
||||
),
|
||||
constraints: BoxConstraints(minWidth: widget.minWidth, maxWidth: widget.maxWidth, maxHeight: widget.maxHeight ?? double.infinity),
|
||||
decoration: BoxDecoration(
|
||||
color: t.dropdownBackground,
|
||||
border: Border.all(color: t.dropdownBorder),
|
||||
@@ -285,9 +279,9 @@ class _ClideMenuState extends State<ClideMenu> {
|
||||
Widget _row(int index, ClideMenuEntry entry, SurfaceTokens t) {
|
||||
return switch (entry) {
|
||||
ClideMenuSeparator() => Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 4),
|
||||
child: Container(height: 1, color: t.dividerColor),
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(vertical: 4),
|
||||
child: Container(height: 1, color: t.dividerColor),
|
||||
),
|
||||
ClideMenuItem() => _itemRow(index, entry, t),
|
||||
};
|
||||
}
|
||||
@@ -310,11 +304,10 @@ class _ClideMenuState extends State<ClideMenu> {
|
||||
color: highlighted || (hovered && item.enabled) ? t.listItemHoverBackground : null,
|
||||
child: Row(
|
||||
children: [
|
||||
if (item.leading != null) ...[
|
||||
ClideIcon(item.leading!, size: 14, color: fg),
|
||||
const SizedBox(width: 8),
|
||||
],
|
||||
Expanded(child: ClideText(item.label, fontSize: clideFontSmall, color: fg, maxLines: 1)),
|
||||
if (item.leading != null) ...[ClideIcon(item.leading!, size: 14, color: fg), const SizedBox(width: 8)],
|
||||
Expanded(
|
||||
child: ClideText(item.label, fontSize: clideFontSmall, color: fg, maxLines: 1),
|
||||
),
|
||||
if (item.trailing != null)
|
||||
item.trailing!
|
||||
else if (item.active) ...[
|
||||
|
||||
@@ -61,7 +61,10 @@ class _ClidePaletteState extends State<ClidePalette> {
|
||||
void _syncFromController() {
|
||||
final f = _palette?.filter ?? '';
|
||||
if (_input.text != f) {
|
||||
_input.value = TextEditingValue(text: f, selection: TextSelection.collapsed(offset: f.length));
|
||||
_input.value = TextEditingValue(
|
||||
text: f,
|
||||
selection: TextSelection.collapsed(offset: f.length),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,13 +129,7 @@ class _ClidePaletteState extends State<ClidePalette> {
|
||||
color: tokens.dropdownBackground,
|
||||
border: Border.all(color: tokens.dropdownBorder),
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: tokens.shadowAmbient,
|
||||
blurRadius: 12,
|
||||
offset: const Offset(0, 4),
|
||||
),
|
||||
],
|
||||
boxShadow: [BoxShadow(color: tokens.shadowAmbient, blurRadius: 12, offset: const Offset(0, 4))],
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
@@ -142,11 +139,7 @@ class _ClidePaletteState extends State<ClidePalette> {
|
||||
child: EditableText(
|
||||
controller: _input,
|
||||
focusNode: _focus,
|
||||
style: TextStyle(
|
||||
fontFamily: clideMonoFamily,
|
||||
fontSize: clideFontMono,
|
||||
color: tokens.dropdownForeground,
|
||||
),
|
||||
style: TextStyle(fontFamily: clideMonoFamily, fontSize: clideFontMono, color: tokens.dropdownForeground),
|
||||
cursorColor: tokens.globalFocus,
|
||||
backgroundCursorColor: tokens.globalFocus,
|
||||
maxLines: 1,
|
||||
@@ -194,14 +187,7 @@ class _ClidePaletteState extends State<ClidePalette> {
|
||||
}
|
||||
|
||||
class _PaletteItem extends StatefulWidget {
|
||||
const _PaletteItem({
|
||||
super.key,
|
||||
required this.title,
|
||||
required this.command,
|
||||
required this.onTap,
|
||||
required this.highlighted,
|
||||
this.binding,
|
||||
});
|
||||
const _PaletteItem({super.key, required this.title, required this.command, required this.onTap, required this.highlighted, this.binding});
|
||||
|
||||
final String title;
|
||||
final String command;
|
||||
@@ -230,24 +216,13 @@ class _PaletteItemState extends State<_PaletteItem> {
|
||||
color: selected
|
||||
? tokens.listItemSelectedBackground
|
||||
: _hover
|
||||
? tokens.listItemHoverBackground
|
||||
: null,
|
||||
? tokens.listItemHoverBackground
|
||||
: null,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: ClideText(
|
||||
widget.title,
|
||||
color: selected ? tokens.listItemSelectedForeground : tokens.listItemForeground,
|
||||
),
|
||||
),
|
||||
if (widget.binding != null)
|
||||
ClideText(
|
||||
widget.binding!,
|
||||
fontSize: clideFontCaption,
|
||||
fontFamily: clideMonoFamily,
|
||||
color: tokens.globalTextMuted,
|
||||
),
|
||||
Expanded(child: ClideText(widget.title, color: selected ? tokens.listItemSelectedForeground : tokens.listItemForeground)),
|
||||
if (widget.binding != null) ClideText(widget.binding!, fontSize: clideFontCaption, fontFamily: clideMonoFamily, color: tokens.globalTextMuted),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
@@ -16,13 +16,7 @@ import 'package:flutter/scheduler.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
class ClidePane extends StatefulWidget {
|
||||
const ClidePane({
|
||||
super.key,
|
||||
required this.contributionId,
|
||||
required this.child,
|
||||
this.statusWidget,
|
||||
this.active = true,
|
||||
});
|
||||
const ClidePane({super.key, required this.contributionId, required this.child, this.statusWidget, this.active = true});
|
||||
|
||||
/// The workspace contribution id this pane belongs to (what
|
||||
/// [FocusTracker.activeContributionId] reports when it's focused).
|
||||
|
||||
@@ -13,15 +13,7 @@ import 'typography.dart';
|
||||
/// underneath. `ClidePtyView`, diff views, canvas tabs, graph tabs —
|
||||
/// everything with a "pane header" surface reuses this.
|
||||
class ClidePaneChrome extends StatelessWidget {
|
||||
const ClidePaneChrome({
|
||||
super.key,
|
||||
required this.title,
|
||||
required this.child,
|
||||
this.subtitle,
|
||||
this.leading,
|
||||
this.onClose,
|
||||
this.trailing,
|
||||
});
|
||||
const ClidePaneChrome({super.key, required this.title, required this.child, this.subtitle, this.leading, this.onClose, this.trailing});
|
||||
|
||||
/// Primary label in the header — typically the pane kind + an
|
||||
/// abbreviated path / session name (`terminal — ~/clide`).
|
||||
@@ -52,13 +44,7 @@ class ClidePaneChrome extends StatelessWidget {
|
||||
color: tokens.panelBackground,
|
||||
child: Column(
|
||||
children: [
|
||||
_Header(
|
||||
title: title,
|
||||
subtitle: subtitle,
|
||||
leading: leading,
|
||||
onClose: onClose,
|
||||
trailing: trailing,
|
||||
),
|
||||
_Header(title: title, subtitle: subtitle, leading: leading, onClose: onClose, trailing: trailing),
|
||||
const ClideDivider(),
|
||||
Expanded(child: child),
|
||||
],
|
||||
@@ -85,14 +71,8 @@ class _CloseButton extends StatelessWidget {
|
||||
width: 20,
|
||||
height: 20,
|
||||
alignment: Alignment.center,
|
||||
decoration: BoxDecoration(
|
||||
color: hovered ? tokens.tabCloseHover : null,
|
||||
),
|
||||
child: ClideIcon(
|
||||
const CloseIcon(),
|
||||
size: 10,
|
||||
color: tokens.panelHeaderForeground,
|
||||
),
|
||||
decoration: BoxDecoration(color: hovered ? tokens.tabCloseHover : null),
|
||||
child: ClideIcon(const CloseIcon(), size: 10, color: tokens.panelHeaderForeground),
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -100,13 +80,7 @@ class _CloseButton extends StatelessWidget {
|
||||
}
|
||||
|
||||
class _Header extends StatelessWidget {
|
||||
const _Header({
|
||||
required this.title,
|
||||
required this.subtitle,
|
||||
required this.leading,
|
||||
required this.onClose,
|
||||
required this.trailing,
|
||||
});
|
||||
const _Header({required this.title, required this.subtitle, required this.leading, required this.onClose, required this.trailing});
|
||||
|
||||
final String title;
|
||||
final String? subtitle;
|
||||
@@ -133,31 +107,12 @@ class _Header extends StatelessWidget {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
ClideText(
|
||||
title,
|
||||
fontSize: clideFontCaption,
|
||||
color: tokens.panelHeaderForeground,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
if (subtitle != null)
|
||||
ClideText(
|
||||
subtitle!,
|
||||
fontSize: clideFontCaption,
|
||||
muted: true,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
ClideText(title, fontSize: clideFontCaption, color: tokens.panelHeaderForeground, maxLines: 1, overflow: TextOverflow.ellipsis),
|
||||
if (subtitle != null) ClideText(subtitle!, fontSize: clideFontCaption, muted: true, maxLines: 1, overflow: TextOverflow.ellipsis),
|
||||
],
|
||||
),
|
||||
),
|
||||
if (trailing != null)
|
||||
...trailing!.map(
|
||||
(w) => Padding(
|
||||
padding: const EdgeInsets.only(left: 6),
|
||||
child: w,
|
||||
),
|
||||
),
|
||||
if (trailing != null) ...trailing!.map((w) => Padding(padding: const EdgeInsets.only(left: 6), child: w)),
|
||||
if (onClose != null)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 6),
|
||||
|
||||
@@ -14,14 +14,7 @@ import 'package:clide/src/terminal/terminal.dart';
|
||||
/// `terminal.write()` is the consumer's job (typically a builtin
|
||||
/// extension — see `builtin.terminal` / `builtin.claude`).
|
||||
class ClidePtyView extends StatelessWidget {
|
||||
const ClidePtyView({
|
||||
super.key,
|
||||
required this.terminal,
|
||||
this.label,
|
||||
this.focusNode,
|
||||
this.autofocus = false,
|
||||
this.fontSize = clideFontMono,
|
||||
});
|
||||
const ClidePtyView({super.key, required this.terminal, this.label, this.focusNode, this.autofocus = false, this.fontSize = clideFontMono});
|
||||
|
||||
final Terminal terminal;
|
||||
|
||||
@@ -49,11 +42,7 @@ class ClidePtyView extends StatelessWidget {
|
||||
focusNode: focusNode,
|
||||
autofocus: autofocus,
|
||||
theme: _buildTheme(tokens),
|
||||
textStyle: TerminalStyle(
|
||||
fontSize: fontSize,
|
||||
fontFamily: clideMonoFamily,
|
||||
fontFamilyFallback: clideMonoFamilyFallback,
|
||||
),
|
||||
textStyle: TerminalStyle(fontSize: fontSize, fontFamily: clideMonoFamily, fontFamilyFallback: clideMonoFamilyFallback),
|
||||
padding: const EdgeInsets.all(2),
|
||||
backgroundOpacity: 1,
|
||||
cursorType: TerminalCursorType.block,
|
||||
|
||||
@@ -4,12 +4,7 @@ import 'package:flutter/widgets.dart';
|
||||
/// Thin themed scrollbar. Tier-0 shell; more refined scrolling (velocity
|
||||
/// multiplier, keyboard nav) lands with the editor.
|
||||
class ClideScrollbar extends StatelessWidget {
|
||||
const ClideScrollbar({
|
||||
super.key,
|
||||
required this.controller,
|
||||
required this.child,
|
||||
this.axis = Axis.vertical,
|
||||
});
|
||||
const ClideScrollbar({super.key, required this.controller, required this.child, this.axis = Axis.vertical});
|
||||
|
||||
final ScrollController controller;
|
||||
final Widget child;
|
||||
@@ -22,25 +17,13 @@ class ClideScrollbar extends StatelessWidget {
|
||||
slider: tokens.scrollbarSlider,
|
||||
sliderHover: tokens.scrollbarSliderHover,
|
||||
track: tokens.scrollbarTrack,
|
||||
child: RawScrollbar(
|
||||
controller: controller,
|
||||
thumbColor: tokens.scrollbarSlider,
|
||||
thickness: 8,
|
||||
radius: const Radius.circular(4),
|
||||
child: child,
|
||||
),
|
||||
child: RawScrollbar(controller: controller, thumbColor: tokens.scrollbarSlider, thickness: 8, radius: const Radius.circular(4), child: child),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ScrollbarTheme extends InheritedWidget {
|
||||
const ScrollbarTheme({
|
||||
super.key,
|
||||
required this.slider,
|
||||
required this.sliderHover,
|
||||
required this.track,
|
||||
required super.child,
|
||||
});
|
||||
const ScrollbarTheme({super.key, required this.slider, required this.sliderHover, required this.track, required super.child});
|
||||
|
||||
final Color slider;
|
||||
final Color sliderHover;
|
||||
|
||||
@@ -33,11 +33,7 @@ class _ClideSelectionAreaState extends State<ClideSelectionArea> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return DefaultTextEditingShortcuts(
|
||||
child: SelectableRegion(
|
||||
focusNode: _focusNode,
|
||||
selectionControls: emptyTextSelectionControls,
|
||||
child: widget.child,
|
||||
),
|
||||
child: SelectableRegion(focusNode: _focusNode, selectionControls: emptyTextSelectionControls, child: widget.child),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,13 +5,7 @@ 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,
|
||||
});
|
||||
const ClideSpine({super.key, required this.label, required this.onExpand, this.side = SpineSide.left, this.badgeCount = 0});
|
||||
|
||||
final String label;
|
||||
final VoidCallback onExpand;
|
||||
@@ -34,10 +28,7 @@ class ClideSpine extends StatelessWidget {
|
||||
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,
|
||||
),
|
||||
border: Border(left: side == SpineSide.right ? borderSide : BorderSide.none, right: side == SpineSide.left ? borderSide : BorderSide.none),
|
||||
),
|
||||
child: Stack(
|
||||
children: [
|
||||
@@ -46,11 +37,7 @@ class ClideSpine extends StatelessWidget {
|
||||
angle: side == SpineSide.left ? -math.pi / 2 : math.pi / 2,
|
||||
child: Text(
|
||||
label,
|
||||
style: TextStyle(
|
||||
fontSize: 9,
|
||||
color: tokens.globalTextMuted,
|
||||
letterSpacing: 0.5,
|
||||
),
|
||||
style: TextStyle(fontSize: 9, color: tokens.globalTextMuted, letterSpacing: 0.5),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.clip,
|
||||
),
|
||||
@@ -65,10 +52,7 @@ class ClideSpine extends StatelessWidget {
|
||||
child: Container(
|
||||
width: 6,
|
||||
height: 6,
|
||||
decoration: BoxDecoration(
|
||||
color: tokens.statusInfo,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
decoration: BoxDecoration(color: tokens.statusInfo, shape: BoxShape.circle),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -16,13 +16,7 @@ import 'package:clide/widgets/src/clide_svg_view.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
class ClideSpinner extends StatefulWidget {
|
||||
const ClideSpinner({
|
||||
super.key,
|
||||
this.size = 14,
|
||||
this.color,
|
||||
this.period = const Duration(milliseconds: 1500),
|
||||
this.semanticLabel,
|
||||
});
|
||||
const ClideSpinner({super.key, this.size = 14, this.color, this.period = const Duration(milliseconds: 1500), this.semanticLabel});
|
||||
|
||||
final double size;
|
||||
|
||||
|
||||
@@ -55,10 +55,7 @@ class _ClideStatusIndicatorState extends State<ClideStatusIndicator> {
|
||||
return Semantics(
|
||||
label: label,
|
||||
container: true,
|
||||
child: AnimatedSwitcher(
|
||||
duration: const Duration(milliseconds: 200),
|
||||
child: glyph,
|
||||
),
|
||||
child: AnimatedSwitcher(duration: const Duration(milliseconds: 200), child: glyph),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,16 +5,7 @@ import 'package:flutter/widgets.dart';
|
||||
/// for clide widgets — pulls color, border, and padding from the
|
||||
/// current theme's surface tokens.
|
||||
class ClideSurface extends StatelessWidget {
|
||||
const ClideSurface({
|
||||
super.key,
|
||||
this.child,
|
||||
this.color,
|
||||
this.border,
|
||||
this.padding = EdgeInsets.zero,
|
||||
this.width,
|
||||
this.height,
|
||||
this.borderRadius,
|
||||
});
|
||||
const ClideSurface({super.key, this.child, this.color, this.border, this.padding = EdgeInsets.zero, this.width, this.height, this.borderRadius});
|
||||
|
||||
final Widget? child;
|
||||
final Color? color;
|
||||
|
||||
@@ -15,9 +15,7 @@ class ClideSvgView extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
Widget child;
|
||||
if (assetPath != null) {
|
||||
child = ScalableImageWidget.fromSISource(
|
||||
si: ScalableImageSource.fromSvg(rootBundle, assetPath!),
|
||||
);
|
||||
child = ScalableImageWidget.fromSISource(si: ScalableImageSource.fromSvg(rootBundle, assetPath!));
|
||||
} else if (svgString != null) {
|
||||
final si = ScalableImage.fromSvgString(svgString!);
|
||||
child = ScalableImageWidget(si: si);
|
||||
|
||||
@@ -6,11 +6,7 @@ import 'package:flutter/widgets.dart';
|
||||
|
||||
@immutable
|
||||
class ClideTabItem {
|
||||
const ClideTabItem({
|
||||
required this.id,
|
||||
required this.title,
|
||||
this.icon,
|
||||
});
|
||||
const ClideTabItem({required this.id, required this.title, this.icon});
|
||||
|
||||
final String id;
|
||||
final String title;
|
||||
@@ -18,14 +14,7 @@ class ClideTabItem {
|
||||
}
|
||||
|
||||
class ClideTabBar extends StatelessWidget {
|
||||
const ClideTabBar({
|
||||
super.key,
|
||||
required this.items,
|
||||
required this.activeId,
|
||||
required this.onSelect,
|
||||
this.height = 28,
|
||||
this.semanticContainerLabel,
|
||||
});
|
||||
const ClideTabBar({super.key, required this.items, required this.activeId, required this.onSelect, this.height = 28, this.semanticContainerLabel});
|
||||
|
||||
final List<ClideTabItem> items;
|
||||
final String? activeId;
|
||||
@@ -49,14 +38,7 @@ class ClideTabBar extends StatelessWidget {
|
||||
margin: const EdgeInsets.only(top: 1),
|
||||
color: tokens.tabBarBackground,
|
||||
child: Row(
|
||||
children: [
|
||||
for (final item in items)
|
||||
_Tab(
|
||||
item: item,
|
||||
active: item.id == activeId,
|
||||
onTap: () => onSelect(item.id),
|
||||
),
|
||||
],
|
||||
children: [for (final item in items) _Tab(item: item, active: item.id == activeId, onTap: () => onSelect(item.id))],
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -89,20 +71,12 @@ class _Tab extends StatelessWidget {
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 4),
|
||||
decoration: BoxDecoration(
|
||||
color: bg,
|
||||
border: Border(
|
||||
bottom: BorderSide(
|
||||
color: active ? tokens.tabActiveBorder : const Color(0x00000000),
|
||||
width: 2,
|
||||
),
|
||||
),
|
||||
border: Border(bottom: BorderSide(color: active ? tokens.tabActiveBorder : const Color(0x00000000), width: 2)),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (item.icon != null) ...[
|
||||
ClideIcon(item.icon!, size: 12, color: fg),
|
||||
const SizedBox(width: 6),
|
||||
],
|
||||
if (item.icon != null) ...[ClideIcon(item.icon!, size: 12, color: fg), const SizedBox(width: 6)],
|
||||
ClideText(item.title, color: fg),
|
||||
],
|
||||
),
|
||||
|
||||
@@ -95,10 +95,7 @@ class _ClideTappableState extends State<ClideTappable> {
|
||||
child = DecoratedBox(
|
||||
position: DecorationPosition.foreground,
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(
|
||||
color: _focused ? tokens.globalFocus : const Color(0x00000000),
|
||||
width: 2,
|
||||
),
|
||||
border: Border.all(color: _focused ? tokens.globalFocus : const Color(0x00000000), width: 2),
|
||||
borderRadius: BorderRadius.circular(3),
|
||||
),
|
||||
child: child,
|
||||
@@ -109,16 +106,8 @@ class _ClideTappableState extends State<ClideTappable> {
|
||||
// provider. The reverse nesting would leave Actions as a descendant
|
||||
// of the focused context — unreachable.
|
||||
child = Actions(
|
||||
actions: <Type, Action<Intent>>{
|
||||
ActivateIntent: CallbackAction<ActivateIntent>(onInvoke: _activate),
|
||||
},
|
||||
child: Focus(
|
||||
focusNode: _effectiveFocus,
|
||||
canRequestFocus: enabled,
|
||||
autofocus: widget.autofocus,
|
||||
onFocusChange: _setFocused,
|
||||
child: child,
|
||||
),
|
||||
actions: <Type, Action<Intent>>{ActivateIntent: CallbackAction<ActivateIntent>(onInvoke: _activate)},
|
||||
child: Focus(focusNode: _effectiveFocus, canRequestFocus: enabled, autofocus: widget.autofocus, onFocusChange: _setFocused, child: child),
|
||||
);
|
||||
|
||||
if (widget.tooltip != null) {
|
||||
|
||||
@@ -43,12 +43,7 @@ class ClideText extends StatelessWidget {
|
||||
maxLines: maxLines,
|
||||
overflow: overflow,
|
||||
textAlign: textAlign,
|
||||
style: TextStyle(
|
||||
color: resolved,
|
||||
fontSize: fontSize,
|
||||
fontFamily: fontFamily,
|
||||
fontWeight: fontWeight,
|
||||
),
|
||||
style: TextStyle(color: resolved, fontSize: fontSize, fontFamily: fontFamily, fontWeight: fontWeight),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,18 +79,17 @@ class _ClideToastState extends State<ClideToast> {
|
||||
children: [
|
||||
ClideIcon(style.icon, size: clideIconStandard, color: style.color),
|
||||
const SizedBox(width: clideGapStandard),
|
||||
Flexible(child: ClideText(widget.entry.message, fontSize: clideFontBody, maxLines: 3, overflow: TextOverflow.ellipsis)),
|
||||
Flexible(
|
||||
child: ClideText(widget.entry.message, fontSize: clideFontBody, maxLines: 3, overflow: TextOverflow.ellipsis),
|
||||
),
|
||||
const SizedBox(width: clideGapStandard),
|
||||
Semantics(
|
||||
button: true,
|
||||
label: 'Dismiss notification',
|
||||
child: ClideTappable(
|
||||
onTap: widget.onDismiss,
|
||||
builder: (ctx, hovered, _) => ClideIcon(
|
||||
PhosphorIcons.byName('x'),
|
||||
size: clideIconStandard,
|
||||
color: hovered ? tokens.globalForeground : tokens.globalTextMuted,
|
||||
),
|
||||
builder: (ctx, hovered, _) =>
|
||||
ClideIcon(PhosphorIcons.byName('x'), size: clideIconStandard, color: hovered ? tokens.globalForeground : tokens.globalTextMuted),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
@@ -3,12 +3,7 @@ import 'package:clide/widgets/src/clide_text.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
class ClideTooltip extends StatefulWidget {
|
||||
const ClideTooltip({
|
||||
super.key,
|
||||
required this.message,
|
||||
required this.child,
|
||||
this.showDelay = const Duration(milliseconds: 500),
|
||||
});
|
||||
const ClideTooltip({super.key, required this.message, required this.child, this.showDelay = const Duration(milliseconds: 500)});
|
||||
|
||||
final String message;
|
||||
final Widget child;
|
||||
@@ -49,17 +44,9 @@ class _ClideTooltipState extends State<ClideTooltip> {
|
||||
final showAbove = spaceBelow < 60;
|
||||
|
||||
if (showAbove) {
|
||||
return Positioned(
|
||||
left: target.dx,
|
||||
bottom: screenSize.height - target.dy + 4,
|
||||
child: tooltip,
|
||||
);
|
||||
return Positioned(left: target.dx, bottom: screenSize.height - target.dy + 4, child: tooltip);
|
||||
}
|
||||
return Positioned(
|
||||
left: target.dx,
|
||||
top: target.dy + size.height + 4,
|
||||
child: tooltip,
|
||||
);
|
||||
return Positioned(left: target.dx, top: target.dy + size.height + 4, child: tooltip);
|
||||
},
|
||||
);
|
||||
overlay.insert(_entry!);
|
||||
|
||||
@@ -112,9 +112,7 @@ class _ClideTypeaheadState extends State<ClideTypeahead> {
|
||||
controller: widget.navController,
|
||||
minWidth: 0,
|
||||
maxWidth: widget.maxWidth,
|
||||
entries: [
|
||||
for (final s in items) ClideMenuItem(label: widget.formatLabel?.call(s) ?? s, onSelect: () => widget.onSelect(s)),
|
||||
],
|
||||
entries: [for (final s in items) ClideMenuItem(label: widget.formatLabel?.call(s) ?? s, onSelect: () => widget.onSelect(s))],
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -11,9 +11,7 @@ class PhosphorIconPainter extends ClideIconPainter {
|
||||
|
||||
@override
|
||||
void paint(ui.Canvas canvas, ui.Color color) {
|
||||
final builder = ui.ParagraphBuilder(
|
||||
ui.ParagraphStyle(fontFamily: family, fontSize: 1.0, height: 1.0, textAlign: ui.TextAlign.center),
|
||||
)
|
||||
final builder = ui.ParagraphBuilder(ui.ParagraphStyle(fontFamily: family, fontSize: 1.0, height: 1.0, textAlign: ui.TextAlign.center))
|
||||
..pushStyle(ui.TextStyle(color: color, fontFamily: family))
|
||||
..addText(String.fromCharCode(codePoint));
|
||||
final paragraph = builder.build()..layout(const ui.ParagraphConstraints(width: 1.0));
|
||||
|
||||
@@ -5,13 +5,7 @@ import 'package:flutter/foundation.dart';
|
||||
/// an editor buffer ref).
|
||||
@immutable
|
||||
class MultitabEntry<T> {
|
||||
const MultitabEntry({
|
||||
required this.id,
|
||||
required this.title,
|
||||
required this.payload,
|
||||
this.closeable = true,
|
||||
this.reorderable = true,
|
||||
});
|
||||
const MultitabEntry({required this.id, required this.title, required this.payload, this.closeable = true, this.reorderable = true});
|
||||
|
||||
/// Stable id for this tab. Must be unique within the controller.
|
||||
final String id;
|
||||
@@ -31,12 +25,7 @@ class MultitabEntry<T> {
|
||||
/// reorderable tabs cannot move past a pinned tab on either side.
|
||||
final bool reorderable;
|
||||
|
||||
MultitabEntry<T> copyWith({
|
||||
String? title,
|
||||
T? payload,
|
||||
bool? closeable,
|
||||
bool? reorderable,
|
||||
}) {
|
||||
MultitabEntry<T> copyWith({String? title, T? payload, bool? closeable, bool? reorderable}) {
|
||||
return MultitabEntry<T>(
|
||||
id: id,
|
||||
title: title ?? this.title,
|
||||
|
||||
@@ -76,23 +76,14 @@ class MultitabPane<T> extends StatelessWidget {
|
||||
if (!keepAlive) {
|
||||
// Single-body mode: rebuild on every active change. Key by id
|
||||
// so a stable body widget tree gets a fresh State on switch.
|
||||
return KeyedSubtree(
|
||||
key: ValueKey('multitab-body-${active.id}'),
|
||||
child: bodyBuilder(context, active),
|
||||
);
|
||||
return KeyedSubtree(key: ValueKey('multitab-body-${active.id}'), child: bodyBuilder(context, active));
|
||||
}
|
||||
// Keep-alive mode: every body stays mounted; switching is just
|
||||
// an IndexedStack index change. Bodies preserve their State.
|
||||
final activeIndex = entries.indexWhere((e) => e.id == active.id);
|
||||
return IndexedStack(
|
||||
index: activeIndex < 0 ? 0 : activeIndex,
|
||||
children: [
|
||||
for (final entry in entries)
|
||||
KeyedSubtree(
|
||||
key: ValueKey('multitab-body-${entry.id}'),
|
||||
child: bodyBuilder(context, entry),
|
||||
),
|
||||
],
|
||||
children: [for (final entry in entries) KeyedSubtree(key: ValueKey('multitab-body-${entry.id}'), child: bodyBuilder(context, entry))],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -196,13 +187,7 @@ class _ReorderableTabState<T> extends State<_ReorderableTab<T>> {
|
||||
Widget build(BuildContext context) {
|
||||
final tokens = ClideTheme.of(context).surface;
|
||||
|
||||
final tabContent = _Tab<T>(
|
||||
entry: widget.entry,
|
||||
active: widget.active,
|
||||
onSelect: widget.onSelect,
|
||||
onClose: widget.onClose,
|
||||
tabHeight: widget.tabHeight,
|
||||
);
|
||||
final tabContent = _Tab<T>(entry: widget.entry, active: widget.active, onSelect: widget.onSelect, onClose: widget.onClose, tabHeight: widget.tabHeight);
|
||||
|
||||
Widget result = DragTarget<String>(
|
||||
onWillAcceptWithDetails: (d) {
|
||||
@@ -219,15 +204,13 @@ class _ReorderableTabState<T> extends State<_ReorderableTab<T>> {
|
||||
setState(() => _isDropTarget = false);
|
||||
widget.onReorderTo(d.data);
|
||||
},
|
||||
builder: (context, _, __) => Row(
|
||||
builder: (context, _, _) => Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 2,
|
||||
height: widget.tabHeight,
|
||||
child: ColoredBox(
|
||||
color: _isDropTarget ? tokens.panelActiveBorder : const Color(0x00000000),
|
||||
),
|
||||
child: ColoredBox(color: _isDropTarget ? tokens.panelActiveBorder : const Color(0x00000000)),
|
||||
),
|
||||
tabContent,
|
||||
],
|
||||
@@ -238,10 +221,7 @@ class _ReorderableTabState<T> extends State<_ReorderableTab<T>> {
|
||||
result = Draggable<String>(
|
||||
data: widget.entry.id,
|
||||
axis: Axis.horizontal,
|
||||
feedback: _DragFeedback(
|
||||
title: widget.entry.title,
|
||||
tabHeight: widget.tabHeight,
|
||||
),
|
||||
feedback: _DragFeedback(title: widget.entry.title, tabHeight: widget.tabHeight),
|
||||
childWhenDragging: Opacity(opacity: 0.4, child: tabContent),
|
||||
child: result,
|
||||
);
|
||||
@@ -268,25 +248,13 @@ class _DragFeedback extends StatelessWidget {
|
||||
color: tokens.panelHeader,
|
||||
border: Border.all(color: tokens.panelActiveBorder),
|
||||
),
|
||||
child: ClideText(
|
||||
title,
|
||||
fontSize: 12,
|
||||
color: tokens.tabActiveForeground,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 1,
|
||||
),
|
||||
child: ClideText(title, fontSize: 12, color: tokens.tabActiveForeground, overflow: TextOverflow.ellipsis, maxLines: 1),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _Tab<T> extends StatefulWidget {
|
||||
const _Tab({
|
||||
required this.entry,
|
||||
required this.active,
|
||||
required this.onSelect,
|
||||
required this.onClose,
|
||||
required this.tabHeight,
|
||||
});
|
||||
const _Tab({required this.entry, required this.active, required this.onSelect, required this.onClose, required this.tabHeight});
|
||||
|
||||
final MultitabEntry<T> entry;
|
||||
final bool active;
|
||||
@@ -320,17 +288,14 @@ class _TabState<T> extends State<_Tab<T>> {
|
||||
excludeSemantics: true,
|
||||
child: ClideTappable(
|
||||
onTap: widget.onSelect,
|
||||
builder: (context, _, __) => Container(
|
||||
builder: (context, _, _) => Container(
|
||||
constraints: BoxConstraints(minWidth: 96, maxWidth: 200),
|
||||
height: widget.tabHeight,
|
||||
// Left: text inset (title breathing room).
|
||||
// Right: when there's a close button, match its uniform
|
||||
// icon margin so top/bottom/right are equal — see the
|
||||
// ui-design `geometry.md` "no double-edge padding" rule.
|
||||
padding: EdgeInsets.only(
|
||||
left: clideInsetText,
|
||||
right: widget.onClose != null ? clideInsetIcon : clideInsetText,
|
||||
),
|
||||
padding: EdgeInsets.only(left: clideInsetText, right: widget.onClose != null ? clideInsetIcon : clideInsetText),
|
||||
decoration: BoxDecoration(
|
||||
color: bg,
|
||||
border: Border(
|
||||
@@ -343,13 +308,7 @@ class _TabState<T> extends State<_Tab<T>> {
|
||||
children: [
|
||||
// Left column: title, takes all remaining space.
|
||||
Expanded(
|
||||
child: ClideText(
|
||||
widget.entry.title,
|
||||
fontSize: 12,
|
||||
color: fg,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 1,
|
||||
),
|
||||
child: ClideText(widget.entry.title, fontSize: 12, color: fg, overflow: TextOverflow.ellipsis, maxLines: 1),
|
||||
),
|
||||
// Right column: close icon, fixed natural width.
|
||||
if (widget.onClose != null) ...[
|
||||
@@ -362,15 +321,8 @@ class _TabState<T> extends State<_Tab<T>> {
|
||||
width: clideIconHitTarget,
|
||||
height: clideIconHitTarget,
|
||||
alignment: Alignment.center,
|
||||
decoration: BoxDecoration(
|
||||
color: hovered ? tokens.listItemHoverBackground : null,
|
||||
borderRadius: BorderRadius.circular(2),
|
||||
),
|
||||
child: ClideIcon(
|
||||
const CloseIcon(),
|
||||
size: clideIconMicro,
|
||||
color: hovered ? tokens.globalForeground : tokens.globalTextMuted,
|
||||
),
|
||||
decoration: BoxDecoration(color: hovered ? tokens.listItemHoverBackground : null, borderRadius: BorderRadius.circular(2)),
|
||||
child: ClideIcon(const CloseIcon(), size: clideIconMicro, color: hovered ? tokens.globalForeground : tokens.globalTextMuted),
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -402,9 +354,7 @@ class _AddButton extends StatelessWidget {
|
||||
width: clideControlHeight,
|
||||
height: tabHeight,
|
||||
alignment: Alignment.center,
|
||||
decoration: BoxDecoration(
|
||||
color: hovered ? tokens.listItemHoverBackground : null,
|
||||
),
|
||||
decoration: BoxDecoration(color: hovered ? tokens.listItemHoverBackground : null),
|
||||
child: ClideText('+', fontSize: clideIconStandard, color: hovered ? tokens.globalForeground : tokens.globalTextMuted),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -177,9 +177,7 @@ class _QuickOpenOverlayState extends State<QuickOpenOverlay> {
|
||||
color: tokens.dropdownBackground,
|
||||
border: Border.all(color: tokens.dropdownBorder),
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
boxShadow: [
|
||||
BoxShadow(color: tokens.shadowAmbient, blurRadius: 12, offset: const Offset(0, 4)),
|
||||
],
|
||||
boxShadow: [BoxShadow(color: tokens.shadowAmbient, blurRadius: 12, offset: const Offset(0, 4))],
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
@@ -189,11 +187,7 @@ class _QuickOpenOverlayState extends State<QuickOpenOverlay> {
|
||||
child: EditableText(
|
||||
controller: _input,
|
||||
focusNode: _focus,
|
||||
style: TextStyle(
|
||||
fontFamily: clideMonoFamily,
|
||||
fontSize: clideFontMono,
|
||||
color: tokens.dropdownForeground,
|
||||
),
|
||||
style: TextStyle(fontFamily: clideMonoFamily, fontSize: clideFontMono, color: tokens.dropdownForeground),
|
||||
cursorColor: tokens.globalFocus,
|
||||
backgroundCursorColor: tokens.globalFocus,
|
||||
maxLines: 1,
|
||||
@@ -246,18 +240,13 @@ class _Hint extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
||||
child: ClideText(text, fontSize: clideFontCaption, color: tokens.globalTextMuted),
|
||||
);
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
||||
child: ClideText(text, fontSize: clideFontCaption, color: tokens.globalTextMuted),
|
||||
);
|
||||
}
|
||||
|
||||
class _QuickOpenItem extends StatefulWidget {
|
||||
const _QuickOpenItem({
|
||||
super.key,
|
||||
required this.path,
|
||||
required this.highlighted,
|
||||
required this.onTap,
|
||||
});
|
||||
const _QuickOpenItem({super.key, required this.path, required this.highlighted, required this.onTap});
|
||||
|
||||
final String path;
|
||||
final bool highlighted;
|
||||
@@ -288,15 +277,12 @@ class _QuickOpenItemState extends State<_QuickOpenItem> {
|
||||
color: selected
|
||||
? tokens.listItemSelectedBackground
|
||||
: _hover
|
||||
? tokens.listItemHoverBackground
|
||||
: null,
|
||||
? tokens.listItemHoverBackground
|
||||
: null,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
|
||||
child: Row(
|
||||
children: [
|
||||
ClideText(
|
||||
name,
|
||||
color: selected ? tokens.listItemSelectedForeground : tokens.listItemForeground,
|
||||
),
|
||||
ClideText(name, color: selected ? tokens.listItemSelectedForeground : tokens.listItemForeground),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: ClideText(
|
||||
|
||||
Reference in New Issue
Block a user